Jvm2、JVM的内存模型与GC算法

1、内存模型

  jvm会使用ClassLoader类加载器加载生成的XXX.class文件。将类信息、方法、对象、常量等进行保存在内存里。

如此,jvm的内存模型变分为:

方法区 Java堆 Java栈 本地方法区

同时,GC垃圾回收器就是针对这个内存进行工作的。

方法区:保存类信息、各种类型的常量、类的字段、方法等。通常与永久区(Perm)关联在一起。

Java堆内存:与程序开发最为密切吧,系统中生成的对象对保存在Java堆中,所有线程共享此资源。

同时,也是GC的主要工作区间,对于GC来说又对此块内存进行了分代。

eden s0 s1 tenured

eden区:也叫伊甸园。所有的对象都在这里出生,如果对象过大会直接放到老年代(tenured)。

s0、s1:统称幸存区(survivor)。s0、s1是两块一模一样大小的内存块,与eden区三者统称新生代。

tenured区:就是老年代。接受新生代无法放置的大对象、长久被引用的对象、系统中存活很久的对象。存活很久意思就是在GC一定次数之后都能存活的对象。

Java栈:也叫线程栈。每一个线程都有一个私有的内存,里面保存线程的私有变量、局部变量、对外的引用等。

本地方法区:本地方法???此处有疑问,待填补。

2、GC算法

GC:Garbage Collection,垃圾回收器。主要对象是JVM中的堆内存和永久区。

2.1 引用计数法

以跟对象开始,对于一个对象K,如果别的对象引用了K,那么K的引用计数器就+1。当引用取消时,则-1;那么对象的引用计数器为0,则对象就可以被回收啦。

Jvm2、JVM的内存模型与GC算法_第1张图片

缺点:对象引用伴随着引用计数器的加减,影响性能。并且对于循环引用无法回收吧。

Jvm2、JVM的内存模型与GC算法_第2张图片

2.2 标记-清除

标记清除算算是现代回收算法的基础,到JDK1.8都在使用此思想。此算法分为两步,先以根节点进行标记可达对象,未被标记的对象就属于可回收对象。第二步便是将所有未被标记的对象清除。

Jvm2、JVM的内存模型与GC算法_第3张图片

标记清除算法比引用计数法直观看起来就爽的飞起来。但是也能看出来,他清理完毕之后,内存是零散的。万一后面生成的对象很大或者很小,那么零散的内存就没办法最大化的利用了。所以又有升级版,标记压缩。

2.3 标记-压缩

标记压缩算法是对标记清除算法的升级,解决了内存碎片化的问题。

Jvm2、JVM的内存模型与GC算法_第4张图片

2.4 复制算法

复制算法就是将原有的内存一分为二,大小相等的内存A、内存B。每次只使用其中的一块内存,垃圾回收时,内存A将标记存活的对象复制到内存B中,然后清除内存A。下次垃圾回收时,将内存B的标记对象复制到内存A中,清空内存B。如此反复完成GC操作。

Jvm2、JVM的内存模型与GC算法_第5张图片

这个算法就是新生代最常用的GC方式。

在堆内存中,分为新生代与老年代。新生代中又分为eden、s0、s1。对象都在eden区出生,当eden区内存达到阈值(比如80%)时,就会触发GC操作,eden标记的对象会复制到s0,然后清空eden区。等eden区又满了,GC再次触发,会标记eden、s0区的存活对象,然后复制到s1上,清空eden、s0。之后执行的GC步骤就循环往复了。

如果eden区直接产生一个对象很大,eden剩余的内存不够使用了,就会直接放在老年代;GC一定次数之后,如果每次都标记复制了一个对象,那么也会将此对象放到老年代里。

此算法针对的场景:在内存中产生的对象数量很多、但是生命周期很短。并且这种现象比较频繁,就比较适合使用此策略。

比如新生代,eden中产生的对象生命周期短、数量大、存活的对象少。每次GC时,只需要少量存活对象复制到新内存上,清空其他内存块。这样处理十分干净、速度也快。所以在新生代中,我们设置内存的占比时,将eden设置的高一些,survivor区少一点。占比几乎是eden:s0:s1 = 8:1:1

2.5 分代思想

新生代和老年代是由对象的生命周期长短而论的。新生代产生的对象多、寿命短,老年代一般寿命都比较长。所以新生代比较适合复制算法,老年代则更适合标记-压缩。

你可能感兴趣的:(JVM相关)