对象在JVM内存中的分配与流转
本系列是为面试,更深的可以参看周志明那本国内最好的JVM书(要出第3版啦)
更好的阅读体验【长期有效】
上篇文章讲到内存划分里的年轻代和老年代,接下来聊聊:对象到底是什么时候进入新生代?又什么情况下回进入老年代?
首先看看上篇文章的一段代码:
public class Test {
private static Manager fetcher = new Fetcher();
public static void main(String[] args) {
load();
while (true) {
fetch();
Thread.sleep(1000);
}
}
private static void load() {
Manager manager = new Manager();
manager.load();
}
private static void fetch() {
fetcher.fetch();
}
}
上节我们讲到类变量fetch
引用Fetch
实例对象,是会长期存活在堆内存的。但是如上图,哪怕是这种对象,在刚被创建new Fetch()
来实例化一个对象时,它也是被分配在新生代里的。
那现在假设一个场景,大家知道,load()
执行完毕,该栈帧出栈,会导致没有任何变量引用Manager
实例对象。但是此时就一定会立即发生去回收堆内存中的Manager
实例对象吗?
NONONO!没这么简单,实际上垃圾回收也有它的触发点。
一个比较常见的场景可能是:我们写的代码创建了N多个对象,然后导致堆内存囤积了大量的对象,这些对象都是有被引用,但是现在没有变量引用了。如果此时代码继续运行,需要在新生代中分配一个对象,发现内存空间不够了!
这个时候就会触发一次新生代的垃圾回收,这个过程也被称之为Minor GC
,有的时候我们也叫Young GC
,他会尝试把新生代总那些没有引用的垃圾对象回收掉。
但是话说回来,我们回忆一下上一节的情况,代码中创建的大部分对象,其实都是使用之后就会被立马回收掉的生存周期极短的对象。可能我们会在新生代分配大量的对象,但是使用之后立马就没有变量引用了,此时新生代差不多也满了。然后在分配新对象时,发现新生代内存空间不足触发一次垃圾回收。
接着来看下一个问题,上图这个Fetch
实例对象,它是一个被类变量fetch
长期引用而长期存活的对象。所以虽然新生代可能随着系统运行,不停创建对象,新生代变慢,然后垃圾回收,但是这个Fetch
对象,它是一直会存活在新生代里的。那么此时JVM就有一条规定:
如果一个实例对象在新生代总,成功在15次垃圾回收之后,还没有被回收,那么就会被认为是会长期存活在内存中的对象,然后它就会被转移到堆内存的老年代中。
接下来下一个问题:老年代的那些对象会被垃圾回收吗?
那肯定啊!(不然留着过年?)老年代里的对象也有可能在代码运行过程中,不再被任何人引用,就需要被垃圾回收。与新生代的情况一样,越来越多的对象转移到老年代,一旦老年代也满了,那是不是也要对老年代进行垃圾回收呢。但是这里的细节暂时我们不用考虑,后面也会详解的。
好了!讲完了新生代和老年代的对象分配,这就结束了?当然没有,事实上在对象分配,还有很多其他复杂的机制:
- 新生代垃圾回收之后,因为存活对象太多,导致大量对象直接进入老年代;
- 特别大的超级大对象直接不经过新生代就进入老年代;
- 动态对象年龄判断机制;
- 空间担保机制
这些都会在后面的文章慢慢展开~~~
总结
对于对象内存分配,目前了解程度:
- 对象优先分配在新生代;
- 新生代如果对象满了,触发
Minor GC
回收掉没有变量引用的垃圾对象; - 躲开15次垃圾回收,转移到老年代;
- 老年代也满了,也会触发垃圾回收
还有后续么,hhh
hhh,现在不写java了,有机会更新一下字节码的知识吧,最近在做链路追踪,java要做这个和字节码还有点关系。
链路追踪,使用了哪些技术呀,有什么好的学习资料么,hhh
这里还是只聊算法好一点
算法和技术都是欢迎的hh
这个系统主要是分享面试,把JVM涉及面试的部分通俗讲解,大家看书可能会有些难以理解的地方
谢谢y总hh