先从代码来看,永生代的更迭
测试代码如下:
public static void main(String[] args){
// jdk 1.6 设置 -XX:PermSize=10M -XX:MaxPermSize=10M
// jdk 1.7 1.8只有设置-Xmx20m -Xms20m -XX:-UseGCOverheadLimit才能起作用(这里的-XX:-UseGCOverheadLimit是关闭GC占用时间过长时会报的异常)
List<String> list = new ArrayList<String>();
int i=0;
while(true){
list.add(String.valueOf(i++).intern());
}
}
- jdk1.6测试:
设置-XX:PermSize=10M -XX:MaxPermSize=10M,执行代码,报错:
Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
at java.lang.String.intern(Native Method)
at com.yanming.lession2.main(lession2.java:21)
可以得出结论,在1.6时代,字符串常量存放在永生代中
- jdk1.7 1.8 测试,继续设置-XX:PermSize=10M -XX:MaxPermSize=10M
可以发现一只可以运行下去。
此时,加入设置-Xmx20m -Xms20m
运行结果:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.lang.Integer.toString(Integer.java:401)
at java.lang.String.valueOf(String.java:3099)
at com.yanming.lession2.main(lession2.java:21)
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=10M; support was removed in 8.0
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=10M; support was removed in 8.0
- 从结果看到,增加的常量都放到了堆中,所以限制堆内存以后,不断增加常量,堆内存会溢出。并且出现警告,PermSize和MaxPermSize从8中移除
-
官网有说明
Area: HotSpot
Synopsis: In JDK 7, interned strings are no longer allocated in the permanent generation of the Java heap, but are instead allocated in the main part of the Java heap (known as the young and old generations), along with the other objects created by the application. This change will result in more data residing in the main Java heap, and less data in the permanent generation, and thus may require heap sizes to be adjusted. Most applications will see only relatively small differences in heap usage due to this change, but larger applications that load many classes or make heavy use of the String.intern() method will see more significant differences.
RFE: 6962931
Metaspace
-
移除永久代(PermGen)的工作从JDK1.7就开始了。
-
JDK1.7中,存储在永久代的部分数据就已经转移到了Java Heap或者是 Native Heap。但永久代仍存在于JDK1.7中,并没完全移除,譬如符号引用(Symbols)转移到了native heap;字面量(interned strings)转移到了java heap;类的静态变量(class statics,Class对象、静态变量)转移到了java heap。
-
1.8之后彻底移除永久代,取而代之的是元空间Metaspace(即本地内存,独立于jvm之外),不再占用堆内存 ,它可以通过自动增长来避免JDK7以及前期版本中常见的永久内存错误(java.lang.OutOfMemoryError: PermGen),当然JDK8也提供了一个新的设置Matespace内存
大小的参数,通过这个参数可以设置Matespace内存大小,这样我们可以根据自己
项目的实际情况,避免过度浪费本地内存,达到有效利用。 -
Metaspace用于存放类元数据,并不是存放常量静态变量等,常量跟1.7一样放入堆中
-
元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制,但可以通过以下参数来指定元空间的大小。
-
默认情况下,类元数据只受可用的本地内存限制(容量取决于是32位或是64位操作系统的可用虚拟内存大小)
-
当类元数据区使用量到达MetaspaceSize的时候,会触发垃圾回收,然后回收掉无用的类加载器和class对象
-
-XX:MaxMetaspaceSize=128m,-XX:MaxMetaspaceSize,最大空间,默认是没有限制的.设置最大的元内存空间128m。如果你设置的元内存空间过小,你的应用程序可能得到下面错误:java.lang.OutOfMemoryError: Metadata space
-
-XX:MetaspaceSize,初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值。
-
-XX:MinMetaspaceFreeRatio,在GC之后,最小的Metaspace剩余空间容量的百分比,减少为分配空间所导致的垃圾收集
-
-XX:MaxMetaspaceFreeRatio,在GC之后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾收集
更新原因:
- 字符串放在永久代中(jvm描述方法区是堆的一个逻辑部分),容易出现性能问题和内存溢出。
- 类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出。
- 永久代会为 GC 带来不必要的复杂度,并且回收效率偏低
- Oracle 可能会将HotSpot 与 JRockit 合二为一
参考:
http://www.cnblogs.com/paddix/p/5309550.html
https://www.jianshu.com/p/4b4cecc8e262
https://blog.csdn.net/zhyhang/article/details/17246223/
https://www.jianshu.com/p/4bcb69fbb1fe
https://blog.csdn.net/lz710117239/article/details/78838087