JVM之 方法区、永久代(PermGen space)、元空间(Metaspace)三者的区别

JVM之 方法区、永久代(PermGen space)、元空间(Metaspace)三者的区别文章目录0、前言(JVM运行时区域)1、PermGen(永久代)2、Metaspace(元空间)3、总结0、前言(JVM运行时区域)阅读此文章时,必须已经了解了jvm运行时数据区域。 根据JVM规范,JVM运行时区域大致分为方法区、堆、虚拟机栈、本地方法栈、程序计数器五个部分。1)、方法区方法区是JVM所有线程共享。主要用于存储类的信息、常量池、方法数据、方法代码等…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全家桶1年46,售后保障稳定

0、前言(JVM 运行时区域)

阅读此文章时,必须已经了解了jvm 运行时数据区域。
 
在这里插入图片描述

根据 JVM 规范,JVM 运行时区域大致分为 方法区、堆、虚拟机栈、本地方法栈、程序计数器 五个部分。

1)、方法区
方法区是JVM 所有线程共享。
主要用于存储类的信息、常量池、方法数据、方法代码等。方法区逻辑上属于堆的一部分,但是为了与 进行区分,通常又叫 非堆。 关于 方法区内存溢出 的问题会在下文中详细探讨。

2)、堆

堆内存也是 JVM 所有线程共享的部分,在虚拟机启动的时候就已经创建。所有的对象和数组都在堆上进行分配。这部分空间可通过 GC 进行回收。当申请不到空间时会抛出 OutOfMemoryError 。

下面我们简单的模拟一个堆内存溢出的情况:

import java.util.ArrayList;
import java.util.List;
 
public class HeapOomMock { 
   
    
    List<byte[]> list = new ArrayList<byte[]>();
    int i = 0;
    
    public static void main(String[] args) { 
     
       
        boolean flag = true;
        while (flag){ 
   
            try { 
   
                i++;
                list.add(new byte[1024 * 1024]);//每次增加一个1M大小的数组对象
            }catch (Throwable e){ 
   
                e.printStackTrace();
                flag = false;
                System.out.println("count="+i);//记录运行的次数
            }
        }
    }
}

Jetbrains全家桶1年46,售后保障稳定

运行结果:

img

注意,这里我指定了堆内存的大小为16M,所以这个地方显示的count=14(这个数字不是固定的),至于为什么会是14或其他数字,需要根据 GC 日志来判断。

1、PermGen(永久代)

PermGen , 就是 PermGen space ,全称是 Permanent Generation space ,是指内存的永久保存区域。这块内存主要是被JVM存放Class和Meta信息的, Class 在被 Loader 时就会被放到 PermGen space 中。

绝大部分 Java 程序员应该都见过 java.lang.OutOfMemoryError: PermGen space 这个异常。
这里的 PermGen space 其实指的就是 方法区 。不过 方法区PermGen space又有一定的区别。

  • 方法区 是 JVM 的规范,所有虚拟机 必须遵守的。常见的JVM 虚拟机 Hotspot 、 JRockit(Oracle)、J9(IBM)

  • PermGen space 则是 HotSpot 虚拟机 基于 JVM 规范对 方法区 的一个落地实现, 并且只有 HotSpot 才有 PermGen space

    而如 JRockit(Oracle)、J9(IBM) 虚拟机有 方法区 ,但是就没有 PermGen space

    PermGen space 是 JDK7及之前, HotSpot 虚拟机 对 方法区 的一个落地实现。在JDK8被移除。

  • Metaspace(元空间)是 JDK8及之后,废弃了 PermGen space ,取而代之的是 Metaspace , 这是 HotSpot 虚拟机 对 方法区 的新的落地实现。

JDK6、JDK7 时,方法区 就是 PermGen(永久代)。
JDK8 时,方法区就是 Metaspace(元空间)

由于方法区 主要存储类的相关信息,所以对于动态生成类的情况比较容易出现永久代的内存溢出。

package com.aop8.jvm.test;
 
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
 
public class PermGenOomMock{ 
   

	List<ClassLoader> classLoaderList = new ArrayList<ClassLoader>();
    public static void main(String[] args) { 
   
        URL url = null;       
        try { 
   
            url = new File("/tmp").toURI().toURL();
            URL[] urls = { 
   url};
            while (true){ 
   
                ClassLoader loader = new URLClassLoader(urls);
                classLoaderList.add(loader);
                loader.loadClass("com.aop8.jvm.test.TestDemo");
            }
        } catch (Exception e) { 
   
            e.printStackTrace();
        }
    }
}

运行结果如下:

img

本例中使用的 JDK 版本是7,指定的 PermGen 区的大小为 8M。通过每次生成不同URLClassLoader对象来加载Test类,从而生成不同的类对象,这样就能看到我们熟悉的 java.lang.OutOfMemoryError: PermGen space 异常了。

从此例子中得出:

JDK6 、JDK7 存在 PermGen space

JDK8 中, Hotspot 已经没有 PermGen space ,取而代之是一个叫做 Metaspace(元空间) 。

下面我们就来看看 MetaspacePermGen space 的区别。

2、Metaspace(元空间)

Metaspace(元空间)和 PermGen(永久代)类似,都是对 JVM规范中方法区的一种落地实现

不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存

Oracle 移除PermGen(永久代)从从JDK7 就开始。例如,字符串内部池,已经在JDK7 中从永久代中移除。直到JDK8 的发布将宣告 PermGen(永久代)的终结。

其实,移除 PermGen 的工作从 JDK7 就开始,永久代的部分数据就已经转移到了 Java Heap 或者是 Native Heap。

但永久代仍存在于JDK7 中,并没完全移除,比如:

  • 字面量 (interned strings)转移到 Java heap

  • 类的静态变量(class statics)转移到Java heap

  • 符号引用(Symbols) 转移到 Native heap

必须知道的是 JDK6 、JDK7 依然存在 PermGen space

我们可以通过一段程序来比较 JDK6 、 JDK7 和 JDK8 的区别,以字符串常量为例:

3、JDK6 、JDK7、JDK8 内存溢出的示例

import java.util.ArrayList;
import java.util.List;
 
public class StringOomMock { 
   
    
    static String  base = "string";
    
    public static void main(String[] args) { 
   
        List<String> list = new ArrayList<String>();
        for (int i=0;i< Integer.MAX_VALUE;i++){ 
   
            String str = base + base;
            base = str;
            list.add(str.intern());
        }
    }
}

JDK6 的运行结果:
img

JDK7 的运行结果:
img

JDK8 的运行结果:
img

从运行结果可以得出:

1)、运行时常量池

  • 在 JDK6 ,抛出永久代(PermGen space)异常,说明 运行时常量池 存在于 方法区
  • 在 JDK7、JDK8 抛出堆(Java heap space)异常,说明 运行时常量池 此时在 Java堆 中;

2)、 方法区(永久代元空间):

JDK8 打印ignoring option PermSize=10M; support was removed in 8.0 ... 警告的原因:

  • 我们都知道,JDK8时,永久代已被移除,所以不支持 -XX:PermSize=10M -XX:MaxPermSize=10M 永久代的参数设置。
  • JDK8 的方法区是 元空间,其参数设置是 -XX:MetaspaceSize=N -XX:MaxMetaspaceSize=N
  • 反推证出 JDK6 、 JDK7 时,永久代 还是存在的,否则打印不支持参数设置的警告。

知识扩展:

JDK版本 方法区的实现 运行时常量池所在的位置
JDK6 PermGen space(永久代) PermGen space(永久代)
JDK7 PermGen space(永久代) Heap(堆)
JDK8 Metaspace(元空间) Heap(堆)

4、元空间与本地内存

元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用 本地内存。默认情况下,元空间的大小仅受 本地内存 限制,但可以通过以下参数来指定元空间的大小:

-XX:MetaspaceSize ,初始空间大小:达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值。
-XX:MaxMetaspaceSize,最大空间:默认是没有限制的。

除了上面两个指定大小的选项以外,还有两个与 GC 相关的属性:
-XX:MinMetaspaceFreeRatio,在GC之后,最小的Metaspace剩余空间容量的百分比,减少为分配空间所导致的垃圾收集;
-XX:MaxMetaspaceFreeRatio ,在GC之后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾收集;

现在我们在 JDK8 下重新运行一下上面的代码,不过这次不再指定 PermSize 和 MaxPermSize。而是指定 -XX:MetaspaceSize-XX:MaxMetaspaceSize 的大小。输出结果如下:

img

从输出结果,我们可以看出,这次不再出现永久代溢出,而是出现了元空间的溢出。

5、总结

通过上面分析,大家应该大致了解了 JVM 的内存划分,也清楚了 JDK8 中永久代元空间的转换。不过大家应该都有一个疑问,就是为什么要做这个转换?所以,最后给大家总结以下几点原因:

1)字符串存在永久代中,容易出现性能问题和内存溢出。

2)类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出。

3)永久代会为 GC 带来不必要的复杂度,并且回收效率偏低。

4)Oracle 可能会将HotSpot 与 JRockit 合二为一。

转载于:

http://blog.csdn.net/zhyhang/article/details/17246223/

http://www.cnblogs.com/paddix/p/5309550.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/234727.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • cs是一种重要的网络计算机模式_cs在计算机代表啥

    cs是一种重要的网络计算机模式_cs在计算机代表啥开始之前,先大概说一下吧。计算机网络研究的是理论,讲究的是网络过程中的连接方式,还有协议制定的原因,为什么这么指定,它不是一个简单的敲代码,做码农就好了的,关键是在于创新,先是理解协议为什么这么制定,要怎么样才可以更加好的指定出下一个协议,怎么才可以做出一个好的东西。什么是cscs即:client/server,是服务器客户端结构。是一种“一对多”的模式,一台服务器,处理多个客户端发来的请求,……

    2022年9月2日
    3
  • 苹果电脑navicat premium激活码-激活码分享[通俗易懂]

    (苹果电脑navicat premium激活码)JetBrains旗下有多款编译器工具(如:IntelliJ、WebStorm、PyCharm等)在各编程领域几乎都占据了垄断地位。建立在开源IntelliJ平台之上,过去15年以来,JetBrains一直在不断发展和完善这个平台。这个平台可以针对您的开发工作流进行微调并且能够提供…

    2022年3月28日
    461
  • GC overhead limit exceeded 问题分析与解决

    GC overhead limit exceeded 问题分析与解决今天出现了一个很奇怪的异常:java.lang.OutOfMemoryError:GCoverheadlimitexceeded,超出了GC开销限制。科普了一下,这个是JDK6新添的错误类型。是发生在GC占用大量时间为释放很小空间的时候发生的,是一种保护机制。一般是因为堆太小,导致异常的原因:没有足够的内存。Sun官方对此的定义:超过98%的时间用来做GC并且回收了不到2%…

    2022年5月21日
    61
  • 十进制转换为二,八,十六进制_vb进制转换

    十进制转换为二,八,十六进制_vb进制转换进制转换原理进制转换是人们利用符号来计数的方法。进制转换由一组数码符号和两个基本因素“基数”与“位权”构成。基数是指,进位计数制中所采用的数码(数制中用来表示“量”的符号)的个数。位权是指,进位制中每一固定位置对应的单位值。在知乎有个问题下的解答很不错,可以参考:打开链接他们之间的关系如下:接下来我们一一阐述。一:(二,八,十六进制)转十进…

    2022年10月11日
    0
  • java面试题库app_java的面试宝典

    java面试题库app_java的面试宝典https://blog.csdn.net/tongshuixu8025/article/details/79949157[添加链接描述]加粗样式

    2022年9月19日
    0
  • 怎么用python打开csv文件_Python文本处理之csv-csv文件怎么打开[通俗易懂]

    怎么用python打开csv文件_Python文本处理之csv-csv文件怎么打开[通俗易懂]csv英文全称是Comma-SeparatedValue,字面翻译逗号分隔值,是一种常见的文本格式文档,可用Excel打开,也可用常见的文本编辑器打开。csv文件经常用于在电子表格软件和纯文本之间交互数据。Python内置了csv模块,可以很方便的操作csv文件。下面介绍两种读写csv文件的方法。一、通过reader和writer函数先看一段简单的示例代码:text包含两个列表元素,首先把这两行…

    2022年7月21日
    41

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号