Java中的堆详解(一看就懂系列:用杂物房做比喻)

堆是什么?
1: 堆是一块大内存,是启动JVM执行Java程序时自动分配的。
2:堆的大小可以是默认动态分配,也可以根据自己需要指定设置。
3:实例化的大对象和数组都在这里,对象类型的变量也会在这里完成初始化,静态变量之类的就在常量池(常量池在运行时会把变量放到运行时常量池,然后给栈里面的一个个栈帧去调用,栈内存里面存放着对象和变量和数组的引用,这里不多说栈的东西。)

翻译:堆就是类似一个杂物间,是JVM干活时候创建的,可以自动分配,也可以自己指定要多大的杂物间(自己指定的一般是老年代存活对象的大小的3-4倍大小),JVM干活就是去通过一个个方法(栈帧)去执行一个个动作,有一些方法需要用到一些工具,例如斧头/电锯(大对象,数组等),那就去杂物间拿,没有人会身上随身携带着斧头电锯吧?所以JVM干活要记得斧头电锯在杂物间哪个位置(对象和数组的引用)。
Java中的堆详解(一看就懂系列:用杂物房做比喻)
堆就是一个杂物间,里面什么都有,堆被所有线程共享,也就是所有人都会往堆里放自己的物品,闲置物品多了就要清理,这就是JVM的垃圾收集子系统GC;闲置物品有分类,有的食物等闲置垃圾物品一天就会发臭,必须马上清理,有的属于无毒无害的垃圾,可以两天收一次,有的是还有用的旧物品,一般可以不用清理,所以这就是堆的分代。

堆的分代:

JVM中堆的分代有两种说法:

(1)JDK 8 以前:分为新生代(eden),老年代(old), 永久代(permanent);
所有对象在被new创建的时候都是在新生代,新生代的大部分对象生命周期很短,eden区还会有两个分区(to survivor 和 from survivor ; 也有的人称为 survivor 1 和 servitor 2,其实都是一样的东西),这里采用引用计数法,对象被引用一次增加一个计数,否则减少一个,一旦没有了计数引用,则被认为是不活跃对象,下一步就会被垃圾收集子系统回收掉,在通过N轮GC回收后的对象进入survivor 1 ,再进入到survivor 2,经过一轮轮minor GC (次收集器)磨练后从survivor 2 进入老年代;老年代的回收频率没有那么频繁,老年代的对象生命周期都比较长。永久代放的都是一些静态方法静态变量,一般不会间隔性回收,除非永久代满了,就一次性回收(Full GC 全收集器)。
(2)JDK 8 :取消了永久代这一块内存空间,取而代之的是叫元空间(也就是本地内存!),其他分代不变。具体介绍看这位大佬的博客: https://blog.csdn.net/yh_zeng2/article/details/80800091
Java中的堆详解(一看就懂系列:用杂物房做比喻)

为什么要有堆的分代?

按照我个人的理解,很少对象是从JVM启动开始就一直用到最后的,而且每个方法都用的,95%业务代码中创建的对象,都是生命周期很短暂的用完就可以放弃掉,不必要占用着宝贵的内存,堆的分代是为了更好的回收内存,提高内存使用率,在分代基础上根据每个分代不同使用率的对象采用不同的垃圾回收算法(引用计数法,复制清除,标记清除,标记整理等等),达到最好的回收效果。

垃圾回收子系统的作用?

先看看下面这张杂物间的图,比较一下上面那间杂物间。
Java中的堆详解(一看就懂系列:用杂物房做比喻)
是不是感觉天差地别,如果你是JVM,在第一个杂物堆里好找工具还是第二个堆里好找工具?你的活动范围和手脚利索程度是在第一个杂物间施展得开拳脚还是第二个施展得开?

为什么要有堆呢?

举例:
基本类型 int a 和对象类型 Integer a ;虽然Java提供了自动拆箱和装箱,也就是基本类型和对象类型相互转变,但是其实它们占用的内存空间大小差距很大,大约是 1: 4 左右。
而众所周知基本类型初始化就是在栈中的值栈,所以值栈的内存空间较小,如果需要大内存的话还是要在堆中初始化。如果不区分的话,就一整块的内存,增加了GC回收的难度,所以分堆分代也是更清晰更好管理JVM内存。

好了具体的就写到这里,这里都是根据自己的理解总结出来的,如果有说的不对的地方欢迎指正,谢谢大家,鞠躬!