IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 【JVM】方法区详解(图文代码) -> 正文阅读

[Java知识库]【JVM】方法区详解(图文代码)

方法区

1、方法区存储什么数据?

和Class文件式一 一对应的

类型信息,?如Class(com.wjx.User类)
?法信息,?如Method(?法名称、?法参数列表、?法返回值信息)
字段信息(域信息),?如Field(字段类型,字段名称需要特殊设置才能保存的住)

Code区(字节码指令区),存储的是?法执?对应的字节码指令
?法表(?法调?的时候) 在A类的main?法中去调?B类的method1?法,
是根据B类的?法表去查找合适的?法,进?调?的。
在方法表缓存方法的key–value值,查找更快

静态变量(类变量)—JDK1.7之后,转移到堆中存储

运?时常量池(字符串常量池)—从class中的常量池加载?来—JDK1.7之后,转移到堆中存储

  • 字?量类型
    • 双引号引起来的字符串值,?如"wjx" ----- 会进?字符串常量池(StringPool)
    • final修饰的变量
    • ?final修饰的变量,?如long、double、float
  • 引?类型–>内存地址
    • 类的符号引?
    • ?法
    • 字段

JIT编译器编译之后的代码缓存

如果需要访问?法区中类的其他信息,都必须先获得Class对象,
才能取访问该Class对象关联的?法信息或者字段信息

存储示意图如下,下面的图片显示的是JVM加载类的时候,方法区存储的信息:

在这里插入图片描述

1.1、类型信息(重点)

●类型的全限定名
●超类的全限定名
●直接超接口的全限定名
●类型标志(该类是类类型还是接口类型)
●类的访问描述符(public、 private、 default. abstract. final、 static)

1.2、类型的常量池

存放该类型所用到的常量的有序集合,包括直接常量(如字符串、整数、
浮点数的常量)和对其他类型、字段、方法的符号引用。
常量池中每-一个保 存的常量都有一个索引,就像数组中的字段一样。因为
常量池中保存着所有类型使用到的类型、字段、方法的字符引用,所以它
也是动态连接的主要对象(在动态链接中起到核心作用)。

1.3、字段信息(重点)

  • 字段修饰符(public、 protect. private. default)
  • 字段的类型
  • 字段名称

1.4、方法信息(重点)

方法信息中包含类的所有方法,每个方法包含以下信息:

  • 方法修饰符
  • 方法返回类型
  • 方法名
  • 方法参数个数、类型、顺序等
  • 方法字节码
  • 操作数栈和该方法在栈帧中的局部变量区大小
  • 异常表

1.5、类变量(重点)

指该类所有对象共享的变量,即使没有任何实例对象时,也可以访问的类变量。它们与类进行绑定。

1.6、指向类加载器的引用

每一个被JVM加载的类型,都保存这个类加载器的引用,类加载器动态链接时会用到。

1.7、指向Class实例的引用

类加载的过程中,虚拟机会创建该类型的Class实例,方法区中必须保存对该对象的引用。
通过Class.forName(tring className)来查找获得该实例的引用,然后创建该类的对象。

1.8、 方法表(重点)

为了提高访问效率,JVM可能会对每个装载的非抽象类,都创建一个数组,数组的每个元素是实例可能调用的方法的直接引用,
包括父类中继承过来的方法。这个表在抽象类或者接口中是没有的。

1.9、运行时常量池

(Runtime Constant Pool)
class文件中除了有类的版本、字段、方法、接口等描述信息外,
还有一项信息是常量池,用于存放编译器生成的各种字面常量和符号引用,这部分内容被类加载后进入方法区的运行时常量池中存放。
运行时常量池相对于class文件常量池的另外一个特征具有动态性,可以在运行期间将新的常量放入池中(典型的如String类的intrn0方法)。

2、永久代和元空间的区别是什么?

???1. JDK1.8之前使用的方法区实现是永久代,JDK1.8及以后使用的方法区实现是元空间。

???2.存储位置不同,永久代所使用的内存区域是IVM进程所使用的区域,它的大小受整个JVM的大小所限制。如果内存不够会触发fullGC(一定要避免)
元空间所使用的内存区域是物理内存区域。那么元空间的使用大小只会受物理内存大小的限制。

???3.存储内容不同,永久代存储的信息基本上就是上面方法区存储内容中的数据。
元空间只存储类的元信息,而静态变量和运行时常量池都挪到堆中。

3、为什么要使元空间来替换永久代?

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

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

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

?4. Oracle 计划将HotSpot与JRockit合二为一。

结论.

其实,移除永久代的工作从DK1.7就开始了。
JDK1.7中,存储在永久代的部分数据就已经转移到了Java Heap。
但永久代仍存在于JDK1.7中,并没完全移除,譬如字面量(interned strings).
转移到了java heap;类的静态变量(class statics)转移到了java heap。
在这里插入图片描述

4、方法区异常演示

4.1、类加载导致OOM(内存溢出)异常

类加载太多和运行时常量池太大

1)案例代码
我们现在通过动态生成类来模拟方法区的内存溢出:
重复加载一个类

package com.wjx.test.memory;
public class Test {}

package com.wjx.test.memory;
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;

import java.util.List;
public class PermGenOomMock{ 
     public static void main(String[] args) { 
         URL url = null; 
         List<ClassLoader> classLoaderList = new ArrayList<ClassLoader>(); 
         try { 
             //撑爆异常
             url = new File("/tmp").toURI().toURL(); 
             URL[] urls = {url}; 
             while (true){ 
                 ClassLoader loader = new URLClassLoader(urls);
                 classLoaderList.add(loader); 
                 loader.loadClass("com.wjx.test.memory.Test"); 
             } 
         } catch (Exception e) { 
             e.printStackTrace(); 
         } 
     }
}

2) JDK1.7分析
指定的PermGen区的大小为8M:

在这里插入图片描述
最典型的场景就是,在jsp页面比较多的情况,容易出现永久代内存溢出。

3) JDK1.8+分析
现在我们在JDK 8下重新运行一下案例代码,不过这次不再指定Permsize和MaxPermsize。
而是指定MetaSpaceSize和MaxMetaSpaceSize的大小。输出结果如下:
在这里插入图片描述
从输出结果,我们可以看出,这次不再出现永久代溢出,而是出现了元空间的溢出。.

4.2、字符串OOM异常

1)案例代码
以下这段程序以2的指数级不断的生成新的字符串,这样可以比较快速的消耗内存:

package com.wjx.test.memory;
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()); 
         } 
     }
}

2) JDK1.6
JDK 1.6的运行结果:
在这里插入图片描述
在JDK 1.6下,会出现永久代的内存溢出。

3) JDK1.7
JDK 1.7的运行结果:
在这里插入图片描述
JDK 1.7中,会出现堆内存溢出。结论是: JK 1.7已经将字符串常量由永久代转移到中。

4) JDK1.8+
JDK 1.8的运行结果:
在这里插入图片描述
在JDK 1.8中,也会出现 内存溢出,并且显示JDK 1.8中PermSize和MaxPermGen已经无效。
因此,可以验证JDK 1.8中已经不存在永久代的结论

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-05-05 11:04:09  更:2022-05-05 11:06:17 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 1:20:08-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码