扩展类加载器-------改变JAVA的父优先类加载顺序
一、简介
方法区在JVM中也是一个非常重要的区域,它与堆一样,是被线程共享的区域。在方法区中,存储了每个类的信息(包括类的名称、方法信息、字段信息)、静态变量、常量以及编译器编译后的代码等。
二、方法区结构
2.1、先看classLoader是如何加载class文件和存储文件信息的
当一个classLoder启动的时候,classLoader的生存地点在jvm中的堆,然后它会去主机硬盘上将A.class装载到jvm的方法区,方法区中的这个字节文件会被虚拟机拿来new A字节码(),然后在堆内存生成了一个A字节码的对象,然后A字节码这个内存文件有两个引用一个指向A的class对象,一个指向加载自己的classLoader。那么方法区中的字节码内存块,除了记录一个class自己的class对象引用和一个加载自己的ClassLoader引用之外,还记录了什么信息呢??见下图:
2.2、方法区关键信息介绍
1.类信息:修饰符(public final)
是类还是接口(class,interface)
类的全限定名(Test/ClassStruct.class)
直接父类的全限定名(java/lang/Object.class)
直接父接口的权限定名数组(java/io/Serializable)
也就是 public final class ClassStruct extends Object implements Serializable这段描述的信息提取
2.字段信息:修饰符(pirvate)
字段类型(java/lang/String.class)
字段名(name)
也就是类似private String name;这段描述信息的提取
3.方法信息:修饰符(public static final)
方法返回值(java/lang/String.class)
方法名(getStatic_str)
参数需要用到的局部变量的大小还有操作数栈大小(操作数栈我们后面会讲)
方法体的字节码(就是花括号里的内容)
异常表(throws Exception)
也就是对方法public static final String getStatic_str ()throws Exception的字节码的提取
4.常量池:
4.1.直接常量:
1.1CONSTANT_INGETER_INFO整型直接常量池public final int CONST_INT=0;
1.2CONSTANT_String_info字符串直接常量池 public final String CONST_STR="CONST_STR";
1.3CONSTANT_DOUBLE_INFO浮点型直接常量池
等等各种基本数据类型基础常量池(待会我们会反编译一个类,来查看它的常量池等。)
4.2.方法名、方法描述符、类名、字段名,字段描述符的符号引用
也就是所有编译器能够被确定,能够被快速查找的内容都存放在这里,它像数组一样通过索引访问,就是专门用来做查找的。
编译时就能确定数值的常量类型都会复制它的所有常量到自己的常量池中,或者嵌入到它的字节码流中。作为常量池或者字节码流的一部分,编译时常量保存在方法区中,就和一般的类变量一样。但是当一般的类变量作为他们的类型的一部分数据而保存的时候,编译时常量作为使用它们的类型的一部分而保存
5.类变量(静态变量):
就是静态字段( public static String static_str="static_str";)
虚拟机在使用某个类之前,必须在方法区为这些类变量分配空间。
6.一个到classLoader的引用,通过this.getClass().getClassLoader()来取得为什么要先经过class呢?思考一下,看一下上面的图,再回来思考。(class A 对象拥有A字节码和加载它的加载器地址引用)
7.一个到class对象的引用,这个对象存储了所有这个字节码内存块的相关信息。所有你能够看到的区域,比如:类信息,你可以通过this.getClass().getName()取得
所有的方法信息,可以通过this.getClass().getDeclaredMethods(),字段信息可以通过this.getClass().getDeclaredFields(),等等,所有在字节码中你想得到的,调用的,通过class这个引用基本都能够帮你完成。因为他就是字节码在内存块在堆中的一个对象
8.方法表,如果学习c++的人应该都知道c++的对象内存模型有一个叫虚表的东西,java本来的名字就叫c++- -,它的方法表其实说白了就是c++的虚表,它的内容就是这个类的所有实例可能被调用的所有实例方法的直接引用。也是为了动态绑定的快速定位而做的一个类似缓存的查找表,它以数组的形式存在于内存中。不过这个表不是必须存在的,取决于虚拟机的设计者,以及运行虚拟机的机器是否有足够的内存。
三、方法区与永久代的关系
很多文章里喜欢把方法区等同与永久代,永久代既然没了,方法区也就没了。但我认为方法区只是一种逻辑上的概念,永久代指物理上的堆内存的一块空间,这块实际的空间完成了方法区存储字节码、静态变量、常量的功能等等。既然如此,现在元空间也可以认为是新的方法区的实现了。
方法区也叫永久代。在过去(自定义类加载器还不是很常见的时候),类大多是”static”的,很少被卸载或收集,因此被称为“永久的(Permanent)”。虽然Java 虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做Non-Heap(非堆),目的应该是与Java 堆区分开来。同时,由于类class是JVM实现的一部分,并不是由应用创建的,所以又被认为是“非堆(non-heap)”内存。HotSpot 虚拟机的设计团队选择把GC 分代收集扩展至方法区,或者说使用永久代来实现方法区而已。对于其他虚拟机(如BEA JRockit、IBM J9 等)来说是不存在永久代的概念的。
永久代也是各个线程共享的区域,它用于存储已经被虚拟机加载过的类信息,常量,静态变量(JDK7中被移到Java堆),即时编译期编译后的代码(类方法)等数据。这里要讲一下运行时常量池,它是方法区的一部分,用于存放编译期生成的各种字面量和符号引用(其实就是八大基本类型的包装类型和String类型数据(JDK7中被移到Java堆))(官方文档说明: 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)。
在JDK1.7中的HotASpot中,已经把原本放在方法区的字符串常量池移到java heap中。
- 将interned String移到Java堆中(示例见《JVM体系结构之七:持久代、元空间(Metaspace) 常量池==了解String类的intern()方法、常量池介绍、常量池从Perm-->Heap》中的示例)
- 将符号Symbols移到native memory(不受GC管理的内存)
从JDK7开始永久代的移除工作,贮存在永久代的一部分数据已经转移到了Java Heap或者是Native Heap。但永久代仍然存在于JDK7,并没有完全的移除:符号引用(Symbols)转移到了native heap;字面量(interned strings)转移到了java heap;类的静态变量(class statics)转移到了java heap。随着JDK8的到来,JVM不再有PermGen。但类的元数据信息(metadata)还在,只不过不再是存储在连续的堆空间上,而是移动到叫做“Metaspace”的本地内存(Native memory)中。
四、jdk1.6,jdk1.7,jdk1.8变化
常量池主要可以分为以下几种:
(1)静态常量池:即*.class文件中的常量池,class文件中的常量池不仅仅包含字符串/数字这些字面量,还包含类、方法的信息,占用class文件绝大部分空间。这种常量池主要用于存放两大类常量:字面量和符号引用量,字面量相当于Java语言层面常量的概念,如文本字符串,声明为final的常量值等;符号引用则属于编译原理 方面的概念,包括了如下三种类型的常量:类和接口的全限定名、字段名称描述符、方法名称描述符。
类的加载过程中的链接部分的解析步骤就是把符号引用替换为直接引用,即把那些描述符(名字)替换为能直接定位到字段、方法的引用或句柄(地址)。
(2)运行时常量池:虚拟机会将各个class文件中的常量池载入到运行时常量池中,即编译期间生成的字面量、符号引用,总之就是装载class文件。
(3)字符串常量池 :字符串常量池可以理解为运行时常量池分出来的部分。加载时,对于class的静态常量池,如果字符串会被装到字符串常量池中。
(4)整型常量池:Integer,类似字符串常量池,可以理解为运行时常量池分出来的。加载时,对于class的静态常量池装的东西,如果是整型会被装到整型常量池中。
类似的还有Character、Long等等常量池(基本数据类型没有哦)。。。
在永久代移除后,字符串常量池也不再放在永久代了,但是也没有放到新的方法区---元空间里,而是留在了堆里(为了方便回收?)。运行时常量池当然是随着搬家到了元空间里,毕竟它是装静态变量、字节码等信息的,有它的地方才称得上方法区。
常量池,在jdk1.7,jdk1.8迁移从永久代迁移到heap堆中。见《方法区之三:运行时常量池==了解String类的intern()方法、常量池介绍、常量池从Perm-->Heap》
五、垃圾回收
jdk1.6
常量池如何触发的垃圾回收?
Class文件中除了有类的版本、字段、方法、接口等描述等信息外,还有一项信息是常量表(constant_pool table),用于存放编译期已可知的常量,这部分内容将在类加载后进入方法区(永久代)存放。但是Java语言并不要求常量一定只有编译期预置入Class的常量表的内容才能进入方法区常量池,运行期间也可将新内容放入常量池(最典型的String.intern()方法)。
GC的作用主要是用来卸载类和回收常量池,当然有部分方法区,即使永久代(Perm Space)也会一定的回收
jdk1.7,1.8
Metaspace 垃圾回收:
对于僵死的类及类加载器的垃圾回收将在元数据使用到“MaxMetaspaceSize”参数的设定值时进行。
适时地监控和调整元空间对于减小垃圾回收频率和减少延时是很有必要的。持续的元空间垃圾回收说明,可能存在类、类加载器导致的内存泄漏或是大小设置不合适。
扩展类加载器-------改变JAVA的父优先类加载顺序的更多相关文章
- JVM类加载器及Java类的生命周期
预定义类加载器(三种): 启动(Bootstrap)类加载器: 是用本地代码实现的类装入器,它负责将<Java_Runtime_Home>/lib下面的类库加载到内存中(比如rt.jar) ...
- Java的类加载器都有哪些,每个类加载器都有加载那些类,什么是双亲委派模型,是做什么的?
类加载器按照层次,从顶层到底层,分为以下三种: (1)启动类加载器(Bootstrap ClassLoader) 这个类加载器负责将存放在JAVA_HOME/lib下的,或者被-Xbootclassp ...
- java自定义类加载器
前言 java反射,最常用的Class.forName()方法.做毕设的时候,接收到代码字符串,通过 JavaCompiler将代码字符串生成A.class文件(存放在classpath下,也就是ec ...
- 乐字节Java反射之三:方法、数组、类加载器和类的生命周期
本文承接上一篇:乐字节Java发射之二:实例化对象.接口与父类.修饰符和属性 继续讲述Java反射之三:方法.数组.类加载器 一.方法 获取所有方法(包括父类或接口),使用Method即可. publ ...
- Java虚拟机JVM学习05 类加载器的父委托机制
Java虚拟机JVM学习05 类加载器的父委托机制 类加载器 类加载器用来把类加载到Java虚拟机中. 类加载器的类型 有两种类型的类加载器: 1.JVM自带的加载器: 根类加载器(Bootstrap ...
- java面向对象--类加载器及Class对象
类加载器 jvm 和 类的关系 当调用 java命令运行一个java程序时,会启动一个java虚拟机进程.同一个jvm的所有线程.所有变量都处于同一个进程里,都使用该jvm进程的内存区. jvm进程终 ...
- 深入理解Java类加载器(1):Java类加载原理解析
1 基本信息 每个开发人员对Java.lang.ClassNotFoundExcetpion这个异常肯定都不陌生,这背后就涉及到了java技术体系中的类加载.Java的类加载机制是技术体系中比较核心的 ...
- Java内存管理-掌握自定义类加载器的实现(七)
勿在流沙筑高台,出来混迟早要还的. 做一个积极的人 编码.改bug.提升自己 我有一个乐园,面向编程,春暖花开! 上一篇分析了ClassLoader的类加载相关的核心源码,也简单介绍了ClassLoa ...
- java类加载器和双亲委派模型
一. 类加载器 ClassLoader即常说的类加载器,其功能是用于从Class文件加载所需的类,主要场景用于热部署.代码热替换等场景. 系统提供3种的类加载器:Bootstrap ClassLoad ...
随机推荐
- 微软职位内部推荐-SENIOR PRODUCER
微软近期Open的职位: Role Based in Shanghai, ChinaTitle: ProducerWe are seeking a Senior Producer to lead Pr ...
- execution 表达式
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) th ...
- Mysql InnoDB彻底释放磁盘空间
Innodb数据库对于已经删除的数据只是标记为删除,并不真正释放所占用的磁盘空间,这就导致InnoDB数据库文件不断增长. 如果需要彻底释放磁盘空间,则需要先导出数据,然后删除数据文件,最后导入数据. ...
- 解决position:relative情况下,z-index无效的方法
在实际开发中,div+css经常会碰到层级的问题 其中有个很头痛的就是z-index控制层级时,老是发现z-index不起作用 老杨依据自己的经验,总结出以下步骤: 1.判断被覆盖的层(想要置顶的层) ...
- uc/os初始化
操作系统初始化函数OS_INIT是操作系统在开始运行的最初,对全局变量.任务控制块.就绪表.事件及消息队列等重要数据结构进行的初始化操作,并创建空闲任务.统计任务等系统任务.该函数必须在创建用 ...
- [转载]C#中播放背景音乐几种的方法
最经在写winform程序,其中有用到播放背景音乐 特此收集了一些网上的教程: 1.调用非托管的dll using System.Runtime.InteropServices; //DllImpor ...
- PHP截取字符串,获取长度,获取字符位置的函数
strstr(string,string) = strchr(,) //从前面第一次出现某个字符串的地方截取到最后strrchr(string,string) //从某个字符串从最后出现的位置截取到结 ...
- spoj 39
DP 完全背包问题 的 d[i] = min(d[i], d[i-w]+p) d[i]表示当总重量为i时可装的最小价值 #include <cstdio> #include &l ...
- Jenkins任务启动的后台进程被自动kill
在Jenkins的使用中,遇到过的一个场景是:在web代码更改之后,能自动的部署到测试服务器,我们写了run.sh脚本来重启服务,在使用Jenkins的任务自动跑这个脚本后发现,服务没有起来.开始以为 ...
- linux压缩文件(夹) zip uzip命令的用法
压缩文件(夹) # 压缩列举的文件,格式如下: zip 压缩包名称 文件1 文件2 文件3 ... # 压缩test.txt, a.out文件,并取名为abc.zip $ zip abc.zip te ...