3 JVM对象创建与内存分配机制

本文详细解释了Java对象的创建过程、对象头结构、内存分配策略(栈上和堆上,包括Eden区、老年代),以及内存回收机制(引用计数、可达性分析、引用类型、finalize和无用类判定)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1 对象创建过程

 2 对象头

2.1 对象大小查看

3 对象内存分配

3.1 对象在栈上分配

3.2 对象在堆上分配

3.2.1 对象在Eden区分配

3.2.2 对象进入老年代的机制

大对象直接进入老年代

长期存活的对象直接进入老年代

对象的动态年龄判断

老年代空间担保机制

4 对象内存回收

4.1 引用计数法

4.2 可达性分析算法

4.3 常见引用类型

4.4 finalize()方法最终判定对象是否存活

4.5 如何判断一个类是无用类


1 对象创建过程

 2 对象头

        KlassPointer指针:保存方法区(类元信息的引用)

        Java Class类对象:存在堆中;给Java开发人员获取类信息的对象

2.1 对象大小查看

可以使用如下包查看:

<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol‐core</artifactId>
    <version>0.14</version>
</dependency>
public static void main(String[] args) {
    ClassLayout layout = ClassLayout.parseInstance(new Object());
    System.out.println(layout.toPrintable());

    System.out.println();
    ClassLayout layout1 = ClassLayout.parseInstance(new int[]{});
    System.out.println(layout1.toPrintable());

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

  

        指针压缩:减少每个对象的大小

                          XX:+UseCompressedOops(默认开启:压缩所有指针)

                          XX:+UseCompressedClassPointers(仅压缩KclassPointer)

        为什么要指针压缩?

                1 压缩后32位(实际存64位),降低带宽(数据传递时)、减少GC压力

                2 减少内存消耗

                3 堆内存小于4G,不需要开启指针压缩

                4 堆内存大于32G时,指针压缩会失效(建议堆内存不超过32G)

3 对象内存分配

3.1 对象在栈上分配

public User test1() {
User user = new User();
user.setId(1);
user.setName("zhuge");
//TODO 保存到数据库
return user;
}

public void test2() {
User user = new User();
user.setId(1);
user.setName("zhuge");
//TODO 保存到数据库
}

        上述示例:test2() User对象可以在栈上分配;外部并无调用(无法逃出该方法)

        对象逃逸分析:(-XX:+DoEscapeAnalysis)分析对象动态作用域,当一个对象在方法中被定义后,它可能被外部方法所引用,例如作为调用参数传递到其他地方中

        标量替换:确定该对象不会被外部使用,且对象可以被进一步分解,JVM不会创建该对象,而是将对象拆成多个成员变量

        标量与聚合量:标量不可再拆分(基本数据类型);聚合量可以进一步被拆分

3.2 对象在堆上分配

3.2.1 对象在Eden区分配

        多数情况下,对象在Eden区分配,当Eden区没有足够空间时,会触发MinorGC

        Minor GC/Young GC:指发生新生代的的垃圾收集动作,Minor GC非常频繁,回收速度一般也比较快

        Major GC/Full GC:一般会回收老年代 ,年轻代,方法区的垃圾,Major GC的速度一般会比Minor GC的慢10倍以上

        Eden:From Survior:To Survior=8:1:1(比例自动变化:-XX:+UseAdaptiveSizePolicy;不变:-XX:-UseAdaptiveSizePolicy)

        大量的对象都在Eden区,且99%都会被回收,剩余存活的挪到Survivor区;保证Eden区尽量大survivor区够用即可

3.2.2 对象进入老年代的机制

大对象直接进入老年代

        -XX:PretenureSizeThreshold=1000000 (单位是字节) -XX:+UseSerialGC

长期存活的对象直接进入老年代

         -XX:MaxTenuringThreshold

        对象在Survivor每熬过一次MinorGC,分代年龄就会+1(默认为15岁,CMS收集器默认6岁)

对象的动态年龄判断

        Survivor区一批对象的总大小大于区域的50%(-XX:TargetSurvivorRatio);大于等于这批对象年龄最大值的对象,会被挪到老年代

        对象动态年龄判断机制一般是在minor gc之后触发的

老年代空间担保机制

        Minor GC之前JVM都会计算老年代剩余可用空间;若空间<年轻代所有对象大小之和(包括垃圾对象);-XX:-HandlePromotionFailure 若开启了该参数;会判断可用空间是否大于每次Minor GC进入老年代的平均大小,若不大于,则触发Full GC;若回收完仍没空间,则触发OOM

4 对象内存回收

4.1 引用计数法

        效率高,但很难解决循环引用(每当有一个地方引用它,计数器就加1;当引用失效,计数器就减1;当为0时就会被回收;相互引用时无法变为0)

4.2 可达性分析算法

        GC Root根节点:线程栈本地变量、静态变量、本地方法栈变量等;从根节点向下找,最后没有被引用的对象会被GC回收

4.3 常见引用类型

强引用:普通变量的引用

public static User user = new User();

软引用:正常情况不会被回收,但是GC做完后发现释放不出空间存放新的对象,则会把这些软引用的对象回收掉(页面的缓存)

public static SoftReference<User> user = new SoftReference<User>(new User());

弱引用:GC会直接回收掉

虚引用:基本不用

4.4 finalize()方法最终判定对象是否存活

        对象被回收之前,会判断是否重写finalize(),可以自救一次

4.5 如何判断一个类是无用类

        1 所有对象实例被回收

        2 ClassLoader被回收

        3 Class对象没有别的地方引用

三个条件均要满足

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值