jdk1.8 后没有了永久代(方法区)用元空间(直接内存)来对方法区进行了实现
原来的永久代,被挪到堆内存中
从上述结果可以看出,JDK 1.6下,会出现“PermGen Space”的内存溢出,而在 JDK 1.7和 JDK 1.8 中,会出现堆内存溢出,并且 JDK 1.8中 PermSize 和 MaxPermGen 已经无效。因此,可以大致验证 JDK 1.7 和 1.8 将字符串常量由永久代转移到堆中,并且 JDK 1.8 中已经不存在永久代的结论。现在我们看看元空间到底是一个什么东西?
元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制,但可以通过以下参数来指定元空间的大小:
-XX:MetaspaceSize,初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值。
-XX:MaxMetaspaceSize,最大空间,默认是没有限制的。
除了上面两个指定大小的选项以外,还有两个与 GC 相关的属性:
-XX:MinMetaspaceFreeRatio,在GC之后,最小的Metaspace剩余空间容量的百分比,减少为分配空间所导致的垃圾收集
-XX:MaxMetaspaceFreeRatio,在GC之后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾收集
对于Java8, HotSpots取消了永久代,那么是不是也就没有方法区了呢?当然不是,方法区是一个规范,规范没变,它就一直在。那么取代永久代的就是元空间。它可永久代有什么不同的?存储位置不同,永久代物理是是堆的一部分,和新生代,老年代地址是连续的,而元空间属于本地内存;存储内容不同,元空间存储类的元信息,静态变量和常量池等并入堆中。相当于永久代的数据被分到了堆和元空间中。
总结
通过上面分析,大家应该大致了解了 JVM 的内存划分,也清楚了 JDK 8 中永久代向元空间的转换。不过大家应该都有一个疑问,就是为什么要做这个转换?所以,最后给大家总结以下几点原因:
1、字符串存在永久代中,容易出现性能问题和内存溢出。
2、类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出。
3、永久代会为 GC 带来不必要的复杂度,并且回收效率偏低。
4、Oracle 可能会将HotSpot 与 JRockit 合二为一。
String.intern() 向常量池中添加字符串,如果不存在则添加,如果存在则直接返回
s1 = new String("ddd").intern();
s2 = new String("ddd").intern();
System.out.println(s1 == s2); // true
本地直接内存可以通过-XX:MaxDirectMemorySize指定,如果不指定则默认与java堆最大值一致-Xmx nio 的DirectByteBuffer 会用到本地直接内存
GC Roots 的对象;
虚拟机栈(栈帧中的本地变量表引用的对象)
方法区中静态属性引用的对象
方法区中常量引用的对象
本地方法栈(Native方法)的引用对象
引用:强 软(发生内存溢出前,进行第二次回收,) 弱(下次清除) 虚 (直接清除)添加虚对象,是为了被回收时能够得到系统通知
finalize 析构方法,第二从标记时会去判断是否需要调用(重写了后需要去调用,不保证finalize中的方法一定会成功执行)
展示:
public Class A{
public static A x = null;
protected void finalize(){
super.finalize();
x = this;
}
}
full gc :System.gc()
算法: 标记清除 CMS
标记整理 G1
其它都是复制算法
种类:标记清除 标记整理 复制 分代收集
回收器种类:
年轻代: serial parNew parallelScavenge
老年代: CMS G1 parallelOld serialOld
CMS 通过-XX:+UseCMSCompactAtFullCollection 开关配置 CMS发生 FullGC后可以进行碎片整理
新生代:MinorGC
老年代:MajorGC FullGC