jvm性能调优 - 05对象在JVM内存中的分配和流转

简介: jvm性能调优 - 05对象在JVM内存中的分配和流转

20210417114228539.png


前文回顾


经过上一篇文章铺垫了一些对象分配的基础知识后,想必大家现在都心里非常有数了,咱们平时代码里创建出来的对象,一般就是两种:


一种是短期存活的,分配在Java堆内存之后,迅速使用完就会被垃圾回收


另外一种是长期存活的,需要一直生存在Java堆内存里,让程序后续不停的去使用


第一种短期存活的对象,是在Java堆内存的新生代里的。第二种长期存活的对象,是在Java堆内存的老年代里的。这个结论,想必大家都已经理解了

好,那么接下来我们就来聊聊,对象到底什么时候进入新生代?然后什么情况下会进入老年代?


大部分正常对象都优先在新生代分配内存


首先我们先来看上篇文章中的一段代码,稍微带着大家来理解一个概念:大部分的正常对象,都是优先在新生代分配内存的。

20210418083638685.png


虽然我们看代码知道,类静态变量“fetcher”引用的那个“ReplicaFetcher”对象,是会长期存活在内存里的


但是哪怕是这种对象,其实刚开始你通过“new ReplicaFetcher()”代码来实例化一个对象时,他也是分配在新生代里的。


包括在“loadReplicasFromDisk()”方法中创建的“ReplicaManager”实例对象,也都是一样分配在新生代里的


同样,我们以一张图,来展示一下:


20210418083710675.png


到底什么情况下会触发新生代的垃圾回收


现在咱们来假设一个场景,大家应该都知道,一旦“loadReplicasFromDisk()”方法执行完毕之后,这个方法的栈帧出栈,会导致没有任何局部变量引用那个“ReplicaManager”实例对象了。


此时可能会如下图所示:


20210418083727459.png


那么此时就一定会立即发生垃圾回收,去回收掉Java堆内存里那个没人使用的“ReplicaManager”实例对象吗?

NO

大家别想的那么简单了,实际上垃圾回收他也得有点触发的条件。

其中一个比较常见的场景可能是这样的,假设我们写的代码中创建了N多对象,然后导致Java堆内存里囤积了大量的对象。

然后这些对象都是之前有人引用,比如各种各样的方法中的局部变量,但是现在也都没人引用了。

如下图所示



20210418090828264.png

这个时候,如果新生代我们预先分配的内存空间,几乎都被全部对象给占满了!此时假设我们代码继续运行,他需要在新生代里去分配一个对象,怎么办?发现新生代里内存空间都不够了!


这个时候,就会触发一次新生代内存空间的垃圾回收,新生代内存空间的垃圾回收,也称之为“Minor GC”,有的时候我们也叫“Young GC”,他会尝试把新生代里那些没有人引用的垃圾对象,都给回收掉。


比如上图中,那个“ReplicaManager”实例对象,其实就是没有人引用的垃圾对象


此时就会当机立断,把“ReplicaManager”实例对象给回收掉,腾出更多的内存空间,然后放一个新的对象到新生代里去。


包括上图中那大量的实例对象,其实也都没人引用,在这个新生代垃圾回收的过程中,就会把这些垃圾对象也都回收掉。


其实话说回来,大家自己仔细回忆一下,我们在代码中创建的大部分对象,其实都是这种使用之后立马就可以回收掉的生存周期极短的对象,是不是?


可能我们会在新生代里分配大量的对象,但是使用完之后立马就没人引用了,此时新生代差不多满了


然后要分配新的对象的时候,发现新生代内存空间不足,就会触发一次垃圾回收,然后就把所有垃圾对象给干掉,腾出大量的内存空间


如下图所示:

20210418090949937.png


长期存活的对象会躲过多次垃圾回收?


接着我们来看下一个问题,上图中大家都注意到了“ReplicaFetcher”实例对象,他是一个长期被“Kafka”类的静态变量“fetcher”引用的长期存活的对象。


所以虽然你的新生代可能随着系统的运行,不停的创建对象,然后让新生代变满,接着垃圾回收一次,大量对象被回收掉


但是你的这个“ReplicaFetcher”对象,他确是一直会存活在新生代里的。


因为他一直被“Kafka”类的静态变量给引用了,所以他不会被回收。那么此时JVM就有一条规定了


如果一个实例对象在新生代中,成功的在15次垃圾回收之后,还是没被回收掉,就说明他已经15岁了。


这是对象的年龄,每垃圾回收一次,如果一个对象没被回收掉,他的年龄就会增加1。


所以如果上图中的那个“ReplicaFetcher”对象在新生代中成功躲过10多次垃圾回收,成为一个“老年人”,那么就会被认为是会长期存活在内存里的对象。


然后他会被转移到Java堆内存的老年代中去,顾名思义,老年代就是放这些年龄很大的对象。


我们再来看一张图:


20210418093004632.png


老年代会垃圾回收吗?


接着下一个问题就是,老年代里的那些对象会被垃圾回收吗?


答案是肯定的,因为老年代里的对象也有可能随着代码的运行,不再被任何人引用了,就需要被垃圾回收。


大家可以思考一下,如果随着类似上面的情况,越来越多的对象进入老年代,一旦老年代也满了,是不是就要对老年代垃圾回收了?


没错,这是肯定的,但是暂时我们先不用过多的去考虑这里的细节,后面我们会进行深入剖析。


关于新生代和老年代的对象分配,这就完了吗?


还有人会说,关于新生代和老年代的对象分配,这就结束了吗?

当然不是,我们这里仅仅是相较于之前的文章,更进一步给大家分析了一下对象分配的一些机制。

但是其实在对象分配这块,还有很多其他的复杂机制,比如:


新生代垃圾回收之后,因为存活对象太多,导致大量对象直接进入老年代

特别大的超大对象直接不经过新生代就进入老年代

动态对象年龄判断机制

空间担保机制


这部分内容结合案例,结合真实生产问题,把JVM各种底层细节带出来。


到这里 ,大家对对象内存分配,了解到这个程度就行了,给大家总结一下:


先理解对象优先分配在新生代


新生代如果对象满了,会触发Minor GC回收掉没有人引用的垃圾对象


如果有对象躲过了十多次垃圾回收,就会放入老年代里


如果老年代也满了,那么也会触发垃圾回收,把老年代里没人引用的垃圾对象清理掉

相关文章
|
2月前
|
存储 缓存 监控
|
22天前
|
存储 算法 Java
散列表的数据结构以及对象在JVM堆中的存储过程
本文介绍了散列表的基本概念及其在JVM中的应用,详细讲解了散列表的结构、对象存储过程、Hashtable的扩容机制及与HashMap的区别。通过实例和图解,帮助读者理解散列表的工作原理和优化策略。
30 1
散列表的数据结构以及对象在JVM堆中的存储过程
|
1天前
|
存储 缓存 监控
Docker容器性能调优的关键技巧,涵盖CPU、内存、网络及磁盘I/O的优化策略,结合实战案例,旨在帮助读者有效提升Docker容器的性能与稳定性。
本文介绍了Docker容器性能调优的关键技巧,涵盖CPU、内存、网络及磁盘I/O的优化策略,结合实战案例,旨在帮助读者有效提升Docker容器的性能与稳定性。
16 6
|
8天前
|
存储 缓存 JavaScript
如何优化Node.js应用的内存使用以提高性能?
通过以上多种方法的综合运用,可以有效地优化 Node.js 应用的内存使用,提高性能,提升用户体验。同时,不断关注内存管理的最新技术和最佳实践,持续改进应用的性能表现。
|
17天前
|
Arthas 监控 Java
JVM进阶调优系列(9)大厂面试官:内存溢出几种?能否现场演示一下?| 面试就那点事
本文介绍了JVM内存溢出(OOM)的四种类型:堆内存、栈内存、元数据区和直接内存溢出。每种类型通过示例代码演示了如何触发OOM,并分析了其原因。文章还提供了如何使用JVM命令工具(如jmap、jhat、GCeasy、Arthas等)分析和定位内存溢出问题的方法。最后,强调了合理设置JVM参数和及时回收内存的重要性。
|
19天前
|
缓存 算法 Java
本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制
在现代软件开发中,性能优化至关重要。本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制。通过调整垃圾回收器参数、优化堆大小与布局、使用对象池和缓存技术,开发者可显著提升应用性能和稳定性。
37 6
|
18天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
45 4
|
19天前
|
监控 安全 程序员
如何使用内存池池来优化应用程序性能
如何使用内存池池来优化应用程序性能
|
22天前
|
存储 缓存 Java
结构体和类在内存管理方面的差异对程序性能有何影响?
【10月更文挑战第30天】结构体和类在内存管理方面的差异对程序性能有着重要的影响。在实际编程中,需要根据具体的应用场景和性能要求,合理地选择使用结构体或类,以优化程序的性能和内存使用效率。
|
2月前
|
存储 监控 算法
JVM调优深度剖析:内存模型、垃圾收集、工具与实战
【10月更文挑战第9天】在Java开发领域,Java虚拟机(JVM)的性能调优是构建高性能、高并发系统不可或缺的一部分。作为一名资深架构师,深入理解JVM的内存模型、垃圾收集机制、调优工具及其实现原理,对于提升系统的整体性能和稳定性至关重要。本文将深入探讨这些内容,并提供针对单机几十万并发系统的JVM调优策略和Java代码示例。
55 2