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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 博弈论(Game Theory)入门学习笔记(持续更新)

    博弈论(Game Theory)入门学习笔记(持续更新)博弈论(GameTheory)入门学习笔记课程介绍1-1Taste-Backoff1-2Self-InterestedAgentsandUtilityTheory1-3Define1-4Examples1-5NashEquilibriumIntro1-6StrategicReasoning课程介绍博弈论,又称对策论,是使用严谨的数学模型研究冲突对抗条件下最优决策问题的理论,是研究竞争的逻辑和规律的数学分支。1-1Taste-Backoff以一个经典案例引出博弈论

    2022年10月15日
    2
  • 工作流引擎Activiti入门-01

    工作流引擎Activiti入门-01工作流引擎 Activiti 入门工作流引擎工作流 BPMBPMNActiv 集成 Activiti 新建数据库新建项目 log4j 的配置 mysql 的配置生成 mysql 表流程操作 Activitibpmn 流程定义流程存储 部署流程启动任务查询任务处理工作流引擎工作流是指业务过程的部分或整体在计算机应用环境下的自动化 工作流主要解决的主要问题是 为了实现某个业务目标 利用计算机在多个参与者之间按某种预定规则自动传递文档 信息或者任务 BPMBPM BusinessProc

    2025年6月14日
    3
  • 从零开始学WEB前端——HTML理论讲解

    从零开始学WEB前端——HTML理论讲解????项目介绍先做个自我介绍,本人是一个没人写前端所以就自学前端的后端程序员????。在此项目中我会和大家一起从零基础开始学习前端,从后端程序员的视角来看前端,受限于作者的水平本项目暂时只会更新到前端框架VUE,不会涉及node.js。该项目适合零基础的小白或者和我一样开发网站没人写前端所以自学前端的后端程序员????。该项目的学习顺序是按照我自己学习时总结出来的,其中的每个知识点都是我认真去理解的,同时也查了很多的资料,所有的参考资料我都放在了文章末尾。尊重开源,尊重知识产权。每一个案例我都亲手写过

    2022年5月3日
    42
  • 判断一个字符串是否是回文字符串「建议收藏」

    判断一个字符串是否是回文字符串「建议收藏」#include&lt;string.h&gt;#include&lt;stdio.h&gt;main(){ chara[40];//定义一个存放待判断的字符数组 inti=0,len; printf("pleaseinputyourstring:(length&lt;40):"); gets(a);//逐个读取a数组中的字符 len=strlen(a);//计…

    2022年5月10日
    41
  • java 静态变量 存储_Java中存储的静态方法和静态变量在哪里?「建议收藏」

    java 静态变量 存储_Java中存储的静态方法和静态变量在哪里?「建议收藏」静态方法(实际上所有方法)以及静态变量都存储在PermGen部分,因为它们是反射数据的一部分(类相关数据,而不是与实例相关的)。需要澄清的最新情况:注意,只有变量及其技术值(原语或引用)存储在PermGen空间中。如果静态变量是对象的引用,则该对象本身存储在堆的正常部分(年轻/旧代或幸存者空间)。这些对象(除非它们是类等内部对象)是不存储在PermGen空间。例子:staticinti=1…

    2022年5月2日
    38
  • Java Bean 简介及其应用

    Java Bean 简介及其应用Bean的中文含义是“豆子”,顾名思义JavaBean是一段Java小程序。JavaBean实际上是指一种特殊的Java类,它通常用来实现一些比较常用的简单功能,并可以很容易的被重用或者是插入其他应用程序中去。所有遵循一定编程原则的Java类都可以被称作JavaBean。 一.JavaBean技术概述       JavaBean是基于Java的组件模型,由属性、方法和事件3部

    2022年7月8日
    22

发表回复

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

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