JVM堆内存和非堆内存

1 堆内存和非堆内存
JVM内存划分为堆内存和非堆内存,堆内存分为年轻代(Young Generation)、老年代(Old Generation),非堆内存就一个永久代(Permanent Generation)。
年轻代又分为Eden和Survivor区。Survivor区由FromSpace和ToSpace组成。Eden区占大容量,Survivor两个区占小容量,默认比例是8:1:1。
官方推荐新生代占堆的的3/8,Survivor占新生代的1/10。
堆内存用途:存放的是对象,垃圾收集器就是收集这些对象,然后根据GC算法回收。
非堆内存用途:永久代,也称为方法区,存储程序运行时长期存活的对象,比如类的元数据、方法、常量、属性等。
在JDK1.8版本废弃了永久代,替代的是元空间(MetaSpace),元空间与永久代上类似,都是方法区的实现,他们最大区别是:永久代使用的是JVM的堆内存空间,而元空间使用的是物理内存,直接受到本机的物理内存限制。在后面的实践中,因为笔者使用的是JDK8,所以打印出的GC日志里面就有MetaSpace。
2 JVM堆内部构型(新生代和老年代)
Jdk8中已经去掉永久区,这里为了与时俱进,不再赘余。
JVM堆内存和非堆内存
文章图片

上图演示Java堆内存空间,分为新生代和老年代,分别占Java堆1/3和2/3的空间,新生代中又分为Eden区、Survivor0区、Survivor1区,分别占新生代8/10、1/10、1/10空间。
问题1:什么是Java堆?
回答1:JVM规范中说到:”所有的对象实例以及数组都要在堆上分配”。Java堆是垃圾回收器管理的主要区域,百分之九十九的垃圾回收发生在Java堆,另外百分之一发生在方法区,因此又称之为”GC堆”。根据JVM规范规定的内容,Java堆可以处于物理上不连续的内存空间中,但是逻辑上要求是连续的。
问题2:为什么Java堆要分为新生代和老年代?
回答2:当前JVM对于堆的垃圾回收,采用分代收集的策略。根据堆中对象的存活周期将堆内存分为新生代和老年代。在新生代中,每次垃圾回收都有大批对象死去,只有少量存活。而老年代中存放的对象存活率高。这样划分的目的是为了使 JVM 能够更好的管理堆内存中的对象,包括内存的分配以及回收。java培训
问题3:为什么新生代要分为Eden区、Survivor0区、Survivor1区?
回答3:这是结构与策略相适应的原则,新生代垃圾收集使用的是复制算法(一种垃圾收集算法,Serial收集器、ParNew收集器、Parallel scavenge收集器都是用这种算法),复制算法可以很好的解决垃圾收集的内存碎片问题,但是有一个天然的缺陷,就是要牺牲一半的内存(即任意时刻只有一半内存用于工作),这对于宝贵的内存资源来说是极度奢侈的。新生代在使用复制算法作为其垃圾收集算法的时候,对其做了优化,拿出2/10的新生代的内存作为交换区,称为Survivor0区和Survivor1区(注意:有的博客上称为From Survivor Space和To Survivor Space,这样阐述也是对的,但是容易对初学者形成误导,因为在复制算法中,复制是双向的,没有固定的From和To,这一次是由这一边到另一边,下次就是从另一边到这一边,使用From Survivor Space和To Survivor Space容易让后来学习者误以为复制只能从一边到另一边,当然有的博客中会附加不管从哪边到哪边,起始就是From,终点就是To,即From Survivor Space和To Survivor Space所对应的区循环对调,但是读者不一定想的明白。所以笔者这里使用Survivor0、Survivor1,减少误解)
所以说,新生代在结构上分为Eden区、Survivor0区、Survivor1区,是与其使用的垃圾收集算法(复制算法)相适应的结果。
问题4:关于永久区Permanent Space?
【JVM堆内存和非堆内存】回答4:由于Jdk8中取消了永久区Permanent Space,本文为与时俱进,不再讲述Permanent Space。

    推荐阅读