jvm对象创建和内存分配优化

news2025/2/21 22:05:36

一、创建对象过程

1、类加载检测

虚拟机遇到一条new指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否是否已被加载、解析和初始化过。如果没有,那必须先执行相应的类加载过程。

new指令对应语言层面讲是,new关键字、对象克隆、对象序列化等

2、分配内存

确保类加载完成之后,接下来jvm就需要为新生对象分配内存。对象所需内存的大小其实在类加载完成之后就可以确定了,然后就是从堆空间中划分出一块确定大小的内存给该对象使用

对象内存分配涉及到两个问题:如何划分内存和如何控制并发分配内存

划分内存的方法:

  • 指针碰撞(Bump the Pointer)(默认用指针碰撞):在堆中分配内存中,有一个指针作为对象已分配和为分配内存分界点,当对象分配进来,指针往未分配内存区域挪动该对象相同大小的距离,这个过程叫指针碰撞
  • 空闲列表:(Free List):在堆中分配对象中,如果堆内存的对象已分配区和未分配区特别凌乱,不是那么规整,这时候只能在jvm内部维护一个列表去记录哪些是内存区域是可用的,当有对象分配进来就更新这个列表

解决并发问题的方法:

  • CAS(Compare And Swap):jvm内部采用CAS自旋来保证对象一定会被分配到内存
  • 本地线程分配缓冲(Thread Local Allocation Buffer, TLAB):每个线程在堆中预先分配一小块内存。通过-XX:+/-UseTLAB参数来设定虚拟机是否使用TLAB(JVM默认开启-XX:+UseTLAB),-XX:TLABSize指定TLAB大小

3、初始化

内存分配完成之后,虚拟机需要将分配到内存空间的对象成员变量都初始化为零值(不包括对象头),如果是TLAB,这一工作过程也可以提前至TLAB分配时进行。这一步操作保证了对象的实例字段在java代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值

4、设置对象头

初始化零值后,jvm要对对象进行必要的设置,例如这个对象是哪个类的实例、如何才能找类的元数据信息、对象的哈希码、对象的gc分带年龄等信息。这些信息放在对象的对象头Object Header之中

在HotSpot虚拟机中,对象在内存中存储的布局可分为三个区域:对象头(Header)、示例数据(Instance Data)和对齐填充

  • 对象头:比如hash码,对象所属分带年龄、对象锁、锁状态标志、偏向锁(线程)ID、偏向时间,数组长度(数组对象才有)等
  • 实例数据:存放类的属性数据信息,包括父类的属性信息
  • 对其填充:由于Hotspot虚拟机的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说就是任何对象的大小都必须是8字节的整数倍。对象头已被精心设计成正好是8的整数倍,因此,如果是对象实例数据没有对齐的话,就需要通过对齐填充来补全

找了一张对象头的图片,

5、执行<init>方法

执行<init>方法,即对象按照程序的意愿进行初始化,就是为属性赋程序员指定值,然后执行构造方法。

6、对象的指针压缩

引入查看对象大小的包:

<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.9</version>
</dependency>
/**
 * 查看对象大小
 */
public class ObjectSizeTest {

    public static void main(String[] args) {
        // object对象16B
        // 对象头
            // mark word 8B
            // Klass Pointer 4B
            // 不是数组 数组长度没有
        // 实例数据 没有成员变量
        // 对其填充 要求整个对象大小是8的整数倍 补上4B
        ClassLayout layout = ClassLayout.parseInstance(new Object());
        System.out.println(layout.toPrintable());

        System.out.println();
        // object对象16B
        // 对象头
        // mark word 8B
        // Klass Pointer 4B
        // 数组长度占4B
        // 实例数据 没有成员变量
        // 对其填充 要求整个对象大小是8的整数倍 不需要填充
        ClassLayout layout1 = ClassLayout.parseInstance(new int[]{});
        System.out.println(layout1.toPrintable());

        System.out.println();
        ClassLayout layout2 = ClassLayout.parseInstance(new A());
        System.out.println(layout2.toPrintable());
    }

    // -XX:+UseCompressedOops           默认开启的压缩所有指针
    // -XX:+UseCompressedClassPointers  默认开启的压缩对象头里的类型指针Klass Pointer
    // Oops : Ordinary Object Pointers
    public static class A {
                        // 对象头
                            // mark word 8B
                            // Klass Pointer 4B 如果关闭压缩-XX:-UseCompressedClassPointers或-XX:-UseCompressedOops,则占用8B
        int id;         // 4B
        String name;    // 对象在堆中的指针4B 如果关闭指针压缩-XX:-UseCompressedOops,则占用8B
        byte b;         // 1B 这个有个内部的填充3B
        Object o;       // 对象在堆中的指针4B 如果关闭指针压缩-XX:-UseCompressedOops,则占用8B
                        // 需要对齐填充4B
                        // A对象大小为32B
    }
}

打印结果:

java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total


[I object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           6d 01 00 f8 (01101101 00000001 00000000 11111000) (-134217363)
     12     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
     16     0    int [I.<elements>                             N/A
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total


com.gaorufeng.jvm.ObjectSizeTest$A object internals:
 OFFSET  SIZE               TYPE DESCRIPTION                               VALUE
      0     4                    (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4                    (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4                    (object header)                           61 cc 00 f8 (01100001 11001100 00000000 11111000) (-134165407)
     12     4                int A.id                                      0
     16     1               byte A.b                                       0
     17     3                    (alignment/padding gap)                  
     20     4   java.lang.String A.name                                    null
     24     4   java.lang.Object A.o                                       null
     28     4                    (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total

 什么是java对象的指针压缩?

  1. jdk1.6 update14开始,在64bit操作系统中,jvm支持指针压缩
  2. jvm配置参数:UseCompressedOops,compressed--压缩、oop(ordinary object pointer)--对象指针
  3. 启动指针压缩:-XX:+UseCompressedOops(默认开启),禁止指针压缩:-XX:UseCompressedOops

为什么要进行指针压缩?

节省内存空间,减少总线寻址性能上的消耗,堆内存小于4G时,不需要启用指针压缩;堆内存小于4G时,不需要启用指针压缩

二、对象的内存分配

1、栈上分配

栈上分配是为了减少gc的次数,提高jvm性能,每次栈帧入栈时创建的对象,出栈就可以释放内存,对象自然就被清理了

需要开启两个参数来确定对象是否能在栈上分配

  • 对象逃逸分析:当一个对象在栈帧方法中,分析出没有被外部对象引用到时,确定有可能在栈上分配,有引用到那就不能在栈上分配,逃逸分析参数(-XX:+DoEscapeAnalysis),jdk7之后默认开启,关闭逃逸分析参数(-XX:-DoEscapeAnalysis)

举个例子:

public User test1() {
		User user = new User();
		return user;
	}
	
	public void test2() {
		User user = new User();
	}

 test1()方法的对象的作用范围是不确定的,test2()方法可以确定user对象作用范围只在当前栈帧方法,不会逃逸出当前方法,可以在栈上进行分配

  • 标量替换:栈帧内存区域的可用内存不一定能够存放一整个对象且随机分布,开启标量替换参数(-XX:+EliminateAllocations),jdk7之后默认开启
  • 标量与聚合量:标量即不可被进一步分解的量,而Java的基本数据类型就是标量(如int、long等基本数据类型以及reference类型等),标量的对立就是可以被进一步分解的量,而这种量称之为聚合量,而在Java中对象就是可以被进一步分解的聚合量

栈上分配依赖于逃逸分析和标量替换

栈上分配示例:
 

/**
 * 把堆空间设小一点 如果发生gc那就是分配在堆中,如果没有gc就证明在栈上分配
 * 使用如下参数不会发生GC
 * -Xmx15m -Xms15m -XX:+DoEscapeAnalysis -XX:+PrintGC -XX:+EliminateAllocations
 * 使用如下参数都会发生大量GC
 * -Xmx15m -Xms15m -XX:-DoEscapeAnalysis -XX:+PrintGC -XX:+EliminateAllocations
 * -Xmx15m -Xms15m -XX:+DoEscapeAnalysis -XX:+PrintGC -XX:-EliminateAllocations
 */
public class AllotOnStack {

	public static void allocation() {
		User user = new User();
	}
	
	public static void main(String[] args) {
		long start = System.currentTimeMillis();
		for (int i = 0; i < 100000000; i++) {
			allocation();
		}
		long end = System.currentTimeMillis();
		System.out.println(end - start);
	}
}

2、堆中Eden区分配

大多数情况下,对象在新生代中Eden区分配。当Eden区没有足够内存空间,虚拟机发起一次minor gc

  • minor gc/young gc:新生代垃圾对象的占用内存回收
  • major gc/full gc:回收整个堆和方法区的垃圾对象占用的内存,比minor gc慢10倍以上

新生代和老年代比例1:2,eden区和s0、s1区比例8:1:1

大部分对象都分配eden区,eden区放满了之后触发minor gc,存活的对象放到survivor区中空的区域,下次触发minor gc,继续eden区和非空的survivor区的非垃圾对象往空的survivor区里面放

jvm默认有个参数-XX:+UseAdaptiveSizePolicy(默认开启),会导致这个8:1:1比例自动变化,如果不想这个比例有变化可以设置参数-XX:-UseAdaptiveSizePolicy

示例:

/**
 * eden区大概默认65M
 * 添加运行JVM参数: -XX:+PrintGCDetails
 */
public class GCTest {
	public static void main(String[] args) {
		 byte[] allocation1, allocation2/*, allocation3, allocation4, allocation5, allocation6*/;
	      allocation1 = new byte[62000*1024];

	      //allocation2 = new byte[8000*1024];

	      /*allocation3 = new byte[1000*1024];
	     allocation4 = new byte[1000*1024];
	     allocation5 = new byte[1000*1024];
	     allocation6 = new byte[1000*1024];*/
	}
}

运行结果:

Heap
 PSYoungGen      total 75776K, used 65024K [0x000000076bc00000, 0x0000000771080000, 0x00000007c0000000)
  eden space 65024K, 100% used [0x000000076bc00000,0x000000076fb80000,0x000000076fb80000)
  from space 10752K, 0% used [0x0000000770600000,0x0000000770600000,0x0000000771080000)
  to   space 10752K, 0% used [0x000000076fb80000,0x000000076fb80000,0x0000000770600000)
 ParOldGen       total 173568K, used 0K [0x00000006c3400000, 0x00000006cdd80000, 0x000000076bc00000)
  object space 173568K, 0% used [0x00000006c3400000,0x00000006c3400000,0x00000006cdd80000)
 Metaspace       used 2644K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 280K, capacity 386K, committed 512K, reserved 1048576K

 可以看到eden区被全部占用,继续分配触发minor gc,由于被引用是非垃圾对象,survivor区放不下,放到老年代

/**
 * eden区大概默认65M
 * 添加运行JVM参数: -XX:+PrintGCDetails
 */
public class GCTest {
	public static void main(String[] args) {
		 byte[] allocation1, allocation2/*, allocation3, allocation4, allocation5, allocation6*/;
	      allocation1 = new byte[62000*1024];

	      allocation2 = new byte[8000*1024];

	      /*allocation3 = new byte[1000*1024];
	     allocation4 = new byte[1000*1024];
	     allocation5 = new byte[1000*1024];
	     allocation6 = new byte[1000*1024];*/
	}
}

打印结果:

[GC (Allocation Failure) [PSYoungGen: 64601K->632K(75776K)] 64601K->62640K(249344K), 0.0270385 secs] [Times: user=0.09 sys=0.02, real=0.03 secs] 
Heap
 PSYoungGen      total 75776K, used 9282K [0x000000076bc00000, 0x0000000775000000, 0x00000007c0000000)
  eden space 65024K, 13% used [0x000000076bc00000,0x000000076c472a78,0x000000076fb80000)
  from space 10752K, 5% used [0x000000076fb80000,0x000000076fc1e030,0x0000000770600000)
  to   space 10752K, 0% used [0x0000000774580000,0x0000000774580000,0x0000000775000000)
 ParOldGen       total 173568K, used 62008K [0x00000006c3400000, 0x00000006cdd80000, 0x000000076bc00000)
  object space 173568K, 35% used [0x00000006c3400000,0x00000006c708e010,0x00000006cdd80000)
 Metaspace       used 2644K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 280K, capacity 386K, committed 512K, reserved 1048576K

后面有对象来会继续分配在eden区

/**
 * eden区大概默认65M
 * 添加运行JVM参数: -XX:+PrintGCDetails
 */
public class GCTest {
	public static void main(String[] args) {
		 byte[] allocation1, allocation2, allocation3, allocation4, allocation5, allocation6;
	      allocation1 = new byte[62000*1024];

	      allocation2 = new byte[8000*1024];

	     allocation3 = new byte[1000*1024];
	     allocation4 = new byte[1000*1024];
	     allocation5 = new byte[1000*1024];
	     allocation6 = new byte[1000*1024];
	}
}

打印结果:

[GC (Allocation Failure) [PSYoungGen: 64601K->696K(75776K)] 64601K->62704K(249344K), 0.0411987 secs] [Times: user=0.06 sys=0.03, real=0.04 secs] 
Heap
 PSYoungGen      total 75776K, used 13621K [0x000000076bc00000, 0x0000000775000000, 0x00000007c0000000)
  eden space 65024K, 19% used [0x000000076bc00000,0x000000076c89f3d8,0x000000076fb80000)
  from space 10752K, 6% used [0x000000076fb80000,0x000000076fc2e030,0x0000000770600000)
  to   space 10752K, 0% used [0x0000000774580000,0x0000000774580000,0x0000000775000000)
 ParOldGen       total 173568K, used 62008K [0x00000006c3400000, 0x00000006cdd80000, 0x000000076bc00000)
  object space 173568K, 35% used [0x00000006c3400000,0x00000006c708e010,0x00000006cdd80000)
 Metaspace       used 2644K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 280K, capacity 386K, committed 512K, reserved 1048576K

3、大对象直接进入老年代

大对象就是需要大量连续内存空间的对象(比如数组、字符串)。jvm参数-XX:PretenureSizeThreshold可以设置大对象的大小,如果对象超过设置大小会直接进入老年代,不会进入年轻代,这个参数只在Serial和ParNew两个收集器下有效

比如设置jvm参数:-XX:PretenureSizeThreshold=1000000 (单位是字节) -XX:+UseSerialGC,再执行下上面的第一个程序就会发现大对象直接入老年代

/**
 * eden区大概默认65M
 * 添加运行JVM参数: -XX:+PrintGCDetails -XX:PretenureSizeThreshold=1000000 -XX:+UseSerialGC
 */
public class GCTest {
	public static void main(String[] args) {
		// 6M的对象 设置1M就是大对象-XX:PretenureSizeThreshold=1000000
		byte[] allocation1 = allocation1 = new byte[6000*1024];
	}
}

打印结果(eden区默认会占用一定空间):

Heap
 def new generation   total 78016K, used 4162K [0x00000006c3400000, 0x00000006c88a0000, 0x0000000717800000)
  eden space 69376K,   6% used [0x00000006c3400000, 0x00000006c3810bd0, 0x00000006c77c0000)
  from space 8640K,   0% used [0x00000006c77c0000, 0x00000006c77c0000, 0x00000006c8030000)
  to   space 8640K,   0% used [0x00000006c8030000, 0x00000006c8030000, 0x00000006c88a0000)
 tenured generation   total 173440K, used 6000K [0x0000000717800000, 0x0000000722160000, 0x00000007c0000000)
   the space 173440K,   3% used [0x0000000717800000, 0x0000000717ddc010, 0x0000000717ddc200, 0x0000000722160000)
 Metaspace       used 2644K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 280K, capacity 386K, committed 512K, reserved 1048576K

为什么要这样?

年轻代的minor gc是比较频繁的,复制耗费性能

4、长期存活的对象进入老年代

对象的对象头分带年龄占4个字节(1111),换算成十进制就是15,所以分带年龄不可能超过15.如果对象在eden区分配并且已经经历过一次minor gc后仍然能够存活,并且能被survivor区所容纳,该对象将被移动到survivor区,并将对象的年龄设为1。对象在survivor区每经历一次minor gc,年龄就会加1,当年龄增加到一定程度(默认15,CMS收集器默认6,不同的垃圾收集器会略微有点不同),就会被晋升到老年代中,对象晋升到老年代的年龄阈值,可以通过参数-XX:MaxTenuringThreshold来设置

5、对象动态年龄判断机制

当前对象的survivor区域里(其中一块区域,放对象的那快s区),一批对象的总大小大于这块survivor区域的50%(-XX:TargetSurvivorRatio可以指定),那么此时大于等于这批对象年龄最大值的对象,就可以直接进入老年代了,例如survivor区域里现在有一批对象,年龄1+年龄2+年龄n的多个年龄对象总和超过survivor区域的50%,此时就会把n(含)以上的对象都放入老年代。这个规则其实是希望那些可能是长期存活的对象,尽早进入老年代。对象动态年龄判断机制一般是在minor gc之后触发

6、老年带空间分配担保机制

年轻代每次minor gc之前jvm都会计算下老年代剩余可用空间,如果这个可用空间小于年轻代里现有的所有对象大小之和(包括垃圾对象),就会看一个-XX:-HandlePromotionFailure(jdk1.8默认就设置了)的参数是否设置了,如果有这个参数,就会看看老年代的可用内存大小,是否大于之前每一次minor gc后进入老年代的对象的平均大小。如果上一步结果是小于或者之前说的参数没有设置,那么就会触发一次full gc,对老年代和年轻代一起回收一次垃圾,如果回收完还是没有足够空间存放新的对象就会发生OOM,如果minor gc之后剩余存活的需要挪到老年代的对象大小还是大于老年代可用空间,那么也会触发full gc,full gc完之后如果还没有空间放minor gc之后存活的对象,也会发生OOM

三、内存回收

1、引用计数法

此算法就是当一个对象被引用一次计数器就会加1,引用为0,则认为是可回收垃圾;唯一的问题就是如果是循环引用,那就永远不可能为0,这样会导致内存泄露

2、可达性分析算法

从gc root(线程栈的本地变量、静态变量、本地方法栈的变量等)开始往下找,找被gc root引用的对象,这些对象都是非垃圾对象

3、常见引用类型

  • 强引用:普通变量的引用
public static User user = new User();
  • 软引用:一般情况下不会被回收,当发生gc释放不出来空间了就会被回收;比如对象频繁的创建但又不是特别重要的对象,可用用与大屏展示,可有可无的对象
public static SoftReference<User> user = new SoftReference<User>(new User());
  • 弱引用:弱引用跟没引用一样,会直接被回收,很少用
public static WeakReference user = new WeakReference(new User());
  • 虚引用:也称为幽灵引用或者幻影引用,它是最弱的一种引用关系,几乎不用

4、finalize()方法自救

每个对象都会执行一次finalize()方法,在回收之前都会进行标记

  1. 第一次标记:查看是否有重写finalize()方法,如果没有就直接被回收掉了
  2. 第二次标记:执行finalize()方法,这个时候如果为了不让gc回收,可以让对象与gc root关联(这样每次创建这个对象都会被自救,内存泄漏,迟早会出现OOM)

5、方法区判断无用的类

  1. 该类在堆中实例对象已被回收
  2. 加载该类的ClassLoader已被回收
  3. 该类的java.lang.Class对象没有任何地方被引用,无法在任何地方通过反射访问该类的方法

6、内存泄漏(会出现OOM)

程序内部采用了静态map缓存数据,由于这个对象是静态的,所以一直在old区,随着数据不断增加,map占用内存更大,会触发full gc,但是清理之后map还是被引用,清理不掉,频繁full耗费cpu和内存(可以采用Ehcache对象淘汰结构、LRU)

7、内存溢出(会出现OOM)

新建的对象过大,导致内存被占满了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/729307.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ModaHub魔搭社区:向量数据库Milvus Lite的优势和适配场景

目录 Milvus Lite 的优势 Milvus Lite 的适配场景 如何安装、部署和使用 Milvus Lite? 总结 想要体验世界上最快的向量数据库&#xff1f;缺少专业的工程师团队作为支撑&#xff1f;Milvus 安装环境受限&#xff1f; 别担心&#xff0c;轻量版 Milvus 来啦&#xff01; …

火爆全网,python自动化测试 parametrize参数化+allure测试报告(详细)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 pytest的参数化&a…

1763_gcc编译c语言makefile自动生成工具的Perl实现_Linux

全部学习汇总&#xff1a; GreyZhang/g_makefile: Learn makefile from all kinds of tutorials on the web. Happy hacking and lets find an common way so we may dont need to touch makefile code any more! (github.com) 其实&#xff0c;调试完这个之后觉得之前Windows上…

开源:老朋友,新棋局

在软件开发领域&#xff0c;开源已经成为一股强大的力量&#xff0c;为企业带来了巨大的好处。我深知开源的价值和影响力。其中之一就是降低开发成本。传统的软件开发往往需要庞大的开发团队和昂贵的授权费用&#xff0c;但开源软件将这一切变得通俗易懂。 避免了重复造轮子&a…

ROS-Moveit!配置

文章目录 1. SW2URDF2.Moveit下载及初始化3.自碰撞矩阵 Self-Collisions4.虚拟关节 Virtual Joints&#xff08;不配置&#xff09;5.规划组 Planning Groups添加机械臂规划组添加夹爪规划组 6.机器人姿态 Robot Pose7.末端执行器 End Effectors8.作者信息 Author Information9…

(秋招)面激光slam必备知识--scan context

scan context是一个描述场景的描述符&#xff0c;它之前不是用在slam上面的&#xff0c;但是有人将它用到激光slam上面&#xff0c;发现还可以&#xff0c;于是这个scan context就用来进行激光slam的位置识别(做闭环用的)。 ​ 编辑切换为居中 添加图片注释&#xff0c;不超过…

Python基础综合案例-数据可视化(地图)

今天给大家带来的是Python综合实战开发的数据可视化操作 通过python实现对数据的分析、可视化 数据来源:线上公布数据&#xff0c;需要可私信 前期准备工作&#xff1a;Python可视化准备工作 前期模块安装等前期基础的准备工作大家可以看我之前的文章讲解&#xff0c;有问题可…

< 每日算法 - JavaScript解析:一文解决 “ 买卖股票 ” 系列算法题 >

每日算法 - JavaScript解析&#xff1a;一文解决 “ 买卖股票 ” 系列算法题 一、基础题目> 题目> 解题思路定义操作定义状态动态规划值所需变量完整代码 二、添加条件&#xff1a;当交易次数为 ∞ 时> 题目> 解决思路 三、添加条件&#xff1a;当交易次数为 K nu…

小机器人在现实世界中学会快速驾驶

小机器人在现实世界中学会快速驾驶 —强化学习加上预训练让机器人赛车手加速前进— Without a lifetime of experience to build on like humans have (and totally take for granted), robots that want to learn a new skill often have to start from scratch. Reinforceme…

【案例教程】GIS在地质灾害危险性评估与灾后重建中的实践技术应用及python机器学习灾害易发性评价模型建立与优化

地质灾害是指全球地壳自然地质演化过程中&#xff0c;由于地球内动力、外动力或者人为地质动力作用下导致的自然地质和人类的自然灾害突发事件。由于降水、地震等自然作用下&#xff0c;地质灾害在世界范围内频繁发生。我国除滑坡灾害外&#xff0c;还包括崩塌、泥石流、地面沉…

AI对话宝-智能AI在线问答写作

AI对话宝的工作原理是基于自然语言处理技术。当用户与其进行交互时&#xff0c;AI对话宝会根据用户的输入&#xff0c;通过算法和模型来理解用户的意图&#xff0c;并给出相应的回答&#xff0c;并且系统还可以不断学习并优化其回答&#xff0c;从而提高其交互的效率和准确性。…

批量提取目录下的所有文件名称放至Excel表格中,方便快捷,快来试试吧

批量提取目录下的所有文件名称放至Excel表格中&#xff0c;方便快捷&#xff0c;快来试试吧 Sina Visitor Systemhttps://weibo.com/tv/show/1034:4920955869265935?fromold_pc_videoshow

binwalk-解包工具

一、binwalk介绍 二、安装binwalk # 1. 安装依赖和binwalk ​git clone https://github.com/ReFirmLabs/binwalk.git cd binwalk sudo python ./setup.py uninstall # 如果您有以前安装的 Binwalk 版本&#xff0c;建议您在升级之前将其卸载 sudo ./deps.sh # 安装依赖项 sud…

C语言学习(三十五)---动态内存练习题与柔性数组

经过前面的内容&#xff0c;我们已经对动态内存的知识已经有了相当多了了解&#xff0c;今天我们再做几道有关动态内存的练习题&#xff0c;然后再介绍一下柔性数组&#xff0c;好了&#xff0c;话不多说&#xff0c;开整&#xff01;&#xff01;&#xff01; 动态内存练习题…

上海亚商投顾:沪指失守3200点 两市成交不足8000亿

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 市场情绪 沪指今日延续调整&#xff0c;失守3200点关口&#xff0c;深成指、创业板指盘中均跌超1%。AI概念股集体下挫&#…

你会做接口测试吗?接口测试面试题盲扫(附答案)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 为什么要做接口测…

【跑实验06】如何数据集中加入图像尺寸?如何把tuple格式的坐标按顺序写成四列数据?如何把某一列放到最后?

文章目录 一、如何数据集中加入图像尺寸&#xff1f;二、如何把tuple格式的坐标按顺序写成四列数据&#xff1f;三、如何把某一列放到最后&#xff1f; 一、如何数据集中加入图像尺寸&#xff1f; 部分核心代码如下&#xff1a; image_files [filename for filename in os.l…

Windows | \\wsl.localhost无法访问

Windows | \\wsl.localhost无法访问 在地址栏输入&#xff1a;\\wsl.localhost或者\\wsl.localhost\

解决新版Pyahcrm2023.1.2版本没有manage repositories按钮无法更改依赖源的问题及使用Pycharm安装库的新理解

本文章的理解基于下面两篇文章&#xff1a; https://blog.csdn.net/henu1710252658/article/details/130918206 https://blog.csdn.net/henu1710252658/article/details/82015796 问题一&#xff1a;新版Pyahcrm2023.1.2版本没有manage repositories按钮无法更改依赖源 首先&am…

高效读深度学习代码:如何又快又好的get代码的逻辑与思想

犹豫很久要不要把读代码这个事情专门挑出来写成一篇推文。毕竟读代码嘛&#xff0c;大家可能都会读。而且笔者个人读的和写的代码量也并不足以到指导大家读代码的程度。但笔者还是决定大胆地写一点&#xff1a;就当是给自己设立今后读代码的标准&#xff0c;也将一些之前未能践…