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

这篇具有很好参考价值的文章主要介绍了jvm对象创建和内存分配优化。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、创建对象过程

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

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的整数倍,因此,如果是对象实例数据没有对齐的话,就需要通过对齐填充来补全

找了一张对象头的图片,

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

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时,不需要启用指针压缩

二、对象的内存分配

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

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

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

三、内存回收

1、引用计数法

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

2、可达性分析算法

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

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 Roots关联(这样每次创建这个对象都会被自救,内存泄漏,迟早会出现OOM)

5、方法区判断无用的类

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

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

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

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

新建的对象过大,导致内存被占满了文章来源地址https://www.toymoban.com/news/detail-533817.html

到了这里,关于jvm对象创建和内存分配优化的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • JVM面试题-JVM对象的创建过程、内存分配、内存布局、访问定位等问题详解

    内存分配的两种方式 指针碰撞 适用场合:堆内存 规整 (即没有内存碎片)的情况下。 原理:用过的内存全部整合到一边,没有用过的内存放在另一边,中间有一个分界指针,只需要向着没用过的内存方向将该指针移动对象内存大小位置即可。 使用该分配方式的GC收集器:

    2024年02月08日
    浏览(39)
  • JVM 创建对象时分配内存的几种方法、分配方法的选择

            假设Java堆中内存是绝对规整的,所有被使用过的内存都被放在一边,空闲的内存被放在另一边,中间放着一个指针作为分界点的指示器,那所分配内存就仅仅是把那 个指针向空闲空间方向挪动一段与对象大小相等的距离。         如果Java堆中的内存并不是规

    2024年02月10日
    浏览(31)
  • Java进阶(1)——JVM的内存分配 & 反射Class类的类对象 & 创建对象的几种方式 & 类加载(何时进入内存JVM)& 注解 & 反射+注解的案例

    1.java运行时的内存分配,创建对象时内存分配; 2.类加载的顺序,创建一个唯一的类的类对象; 3.创建对象的方式,new,Class.forName,clone; 4.什么时候加载.class文件进入JVM内存中,看到new,Class.forName; 5.如何加载?双亲委托(委派)机制:安全;AppClassLoader; 6.反射实质:能

    2024年02月14日
    浏览(34)
  • JVM 给对象分配内存空间

    指针碰撞 空闲列表 TLAB 为对象分配空间的任务实际上便等同于把一块确定大小的内存块从Java堆中划分出来。 指针碰撞:(Bump The Pointer) 堆的内存是绝对规整的,内存主要分为两部分,所有使用过的内存被放在一边,空闲的内存被放在另一边,中间放着一个指针作为分界点

    2024年02月11日
    浏览(25)
  • JVM对象在堆内存中是否如何分配?

    1:指针碰撞:内存规整的情况下 2:空闲列表: 内存不规整的情况下 选择那种分配方式 是有 java堆是否规整而决定的。而java堆是否规整是否对应的垃圾回收器是否带有空间压缩整理的能力决定的。 因此当使用Serial,ParNew等带有压缩整理过程的收集器时,系统采用的分配算法是

    2024年02月16日
    浏览(33)
  • JVM 垃圾回收详解之内存分配和回收原则+死亡对象判断方法

    当需要排查各种内存溢出问题、当垃圾收集成为系统达到更高并发的瓶颈时,我们就需要对这些“自动化”的技术实施必要的监控和调节。 Java 的自动内存管理主要是针对对象内存的回收和对象内存的分配。同时,Java 自动内存管理最核心的功能是 堆 内存中对象的分配与回收

    2023年04月19日
    浏览(44)
  • JVM 内存大对象监控和优化实践

    作者:vivo 互联网服务器团队 - Liu Zhen、Ye Wenhao 服务器内存问题是影响应用程序性能和稳定性的重要因素之一,需要及时排查和优化。本文介绍了某核心服务内存问题排查与解决过程。首先在JVM与大对象优化上进行了有效的实践,其次在故障转移与大对象监控上提出了可靠的

    2024年02月10日
    浏览(30)
  • 【Java虚拟机学习2】HotSpot虚拟机下对象的创建及在Java堆中对象的内存分配、布局和对象的访问

    对象的生命周期主要分为创建、使用、销毁这三大阶段。从它被创建的一刻开始,到它被垃圾收集器(Garbage Collector)回收的一刻结束 对象的创建 。包括:1、类的加载 2、内存的分配 3、初始化零值 4、设置对象头 5、执行init方法(具体操作步骤请看上述内容,其中步骤1的类

    2024年02月16日
    浏览(31)
  • 【Android】使用对象池(Object Pool)来缓存已经创建的字节数组,避免频繁地进行内存分配和回收操作提高性能

    在Android中,使用new byte[]创建字节数组是在堆上分配内存,不会直接导致Native内存的增长。但是,如果我们频繁地创建和销毁字节数组,就可能会导致堆内存不足,并触发GC,从而影响应用程序的性能。 在Android中,堆内存的大小是有限制的。如果我们频繁地创建和销毁字节数

    2024年02月09日
    浏览(36)
  • JVM的故事—— 内存分配策略

    堆内存有新生代和老年代,新生代中有一个Eden区和一个Survivor区(from space或者to space)。当有新的对象分配时,会优先分配在Eden区。当Eden区空间不足分配给新对象时,会进行一次minor GC,回收完没有引用的对象后,先考虑把一些Eden区的对象放到Survivor区,如果放不下,就放到老

    2024年02月10日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包