java中JVM与类加载器

JVM虚拟机介绍:
Sun HotSpot VM
这个目前看起来“血统纯正”的虚拟机在最初并非由Sun公司开发,而是由一-家名为"Longview Technologies"的小公司设计的:甚至这个虚拟机最初并非是为Java语言而开发的,它来源于Strongtalk VM,而这款虚拟机中相当多的技术又是来源于一款支持Self语言实现“达到C语言50%以上的执行效率”的目标而设计的虚拟机,Sun公司注意到了这款虚拟机在JIT编译上有许多优秀的理念和实际效果,在1997年收购了Longview Technologies公司, 从而获得了HotSpot VM。

BEA JRockit VM
JRockit VM曾经号称“世界上速度最快的JAVA 虚拟机”它是BEA公司在2002年从Appeal Virtual Machines 公司收购的虚拟机。BEA公司将其发展为一款专 门为服务器硬件和服务器端应用场景高度优化的虚拟机,由于专注于服务器端应用,它可以不太关注程序启动速度,因此JRockit 内部不包含解析器实现,全部代码都靠即时编译器译后执行。除此之外,JRockit的垃圾收集器和Mi ssionControl服务套件等部分的实现,在众多JAVA 虚拟机中也一-直处于 领先水平。

IBM J9 VM
IBM J9 VM并不是IBM公司唯一的JAVA虚拟机,不过是目前其主力发展的JAVA 虚拟机。IBM J9 VM原本是内部开发代号,正式名称是“IBM Technology for Java Virtual Machine” ,它最早是由IBM Ottawa实验室一个SmallTalk的虚拟机扩展来的。那时候,这个虚拟机有一个bug是因为8K值定义错误引起,工程师们花了很长时间终于发现并解决了这个错误,此后这个版本的虚拟机就被称为K8了。于是,后来出现的支持Java 这个版本的虚拟机就被称为J9了。J9市场定位与HotSpot 比较接近,从服务器到桌面应用再到嵌入式都全面考虑的多用途虚拟机。

虚拟机速度决定因素: 编译效率,垃圾收集器算法,虚拟机内部结构
 

JVM运行时数据区结构

Java虛拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域,这些区域都各司其职,以及创建和销毁的时间,在《Java虚拟机规范(Java SE 8版)》的规定中,Java虛拟机所管理的内存包括五大部分区域:分别是程序计数器、虚拟机栈、本地方法栈、堆和方法区。

java中JVM与类加载器
程序计数器:里面存放的是将要执行的指令地址(下一条指令地址)
程序计数器是一块较小的内存空间,指的是当前线程所执行的字节码的行号指示器。通俗一点来说,每个线程在运行期都有独立的程序计数器,程序计数器是属于线程隔离的数据区。多线程是通过线程间切换抢夺CPU分配的时间片来竞争执行的( 多核CPU来说指的是一个内核),一个CPU处理器只会在同一时间执行一条线程指令。

虚拟机栈:
Java虛拟机栈也是线程独立的,多个线程有独立的Java虚拟机栈,栈的生命周期与线程相同,在线程启动时被创建,线程结束时被销毁,栈是用来存储Java方法运行时数据的。
在栈中存储的数据结构是一个数据单位来体现的,这个数据单位称为栈帧(Stack Frame),当程序执行一个方法时,会创建一个栈帧,我们称为入栈(先进后出),当方法执行结束后,栈帧就会被销毁,我们称为出栈。在一个栈帧里,用于存储局部变量表、操作数栈、动态链接、方法出口和一些额外的附加信息。
栈帧是虚拟机在方法调用执行时存储在虚拟机栈的数据结构,也可以称为栈元素,一个线程对应一个虚拟机栈,一个方法对应一个栈帧,一个栈帧概念结构如图所示。
java中JVM与类加载器
本地方法栈
本地方法栈(Native Method Stack) 与虚拟机栈所发挥的作用是非常相似的,它们之间的区别不过是虚拟机栈为虚拟机运行Java方法(字节码)服务,而本地方法栈则为虚拟机使用到的Native方法服务。
在虚拟机规范中对本地方法栈中方法使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。甚至有的虚拟机(Sun Hotspot)直接把本地方法栈和虚拟机栈合二为一。与虚拟机栈一-样,本地方法栈区域也会抛StackOverflowError和OutOfMemoryError异常。

Java堆
Java堆(Java Heap) 是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。

Java堆内部结构
java中JVM与类加载器
新生区
新生区是类的诞生、成长、消亡的区域。新生区又分为两部分:伊甸区(Eden space) 和幸存者区(Survivor pace) ,所有的类都是在伊甸区被new出来的。幸存区有两个: 0区(Survivor 0 space) 和1区(Survivor 1 space) 。
当伊甸园的空间用完时,程序又需要创建对象,JVM的垃圾回收器将对伊甸园进行垃圾回收(MinorGC) ,将伊甸园中的剩余对象移动到幸存0区。若幸存0区也满了,再对该区进行垃圾回收,然后移动到1区。那如果1去也满了呢?再移动到养老区。若养老区也满了,那么这个时候将产生Major GC(FullGC),进行养老区的内存清理。若养老区执行Full GC之后发现依然无法进行对象的保存, 就会产生00M异常“OutOfMemoryError” 。
如果出现java. lang. OutOfMemoryError: Java heap space异常,说明Java虚拟机的堆内存不够。
原因有二:
a. Java虚拟机的堆内存设置不够,可以通过参数- Xms、-Xmx来 调整。
b.代码中创建了大量大对象,并且长时间不能被垃圾收集器收集(存在被引用)。

养老区
养老区用于保存从新生区筛选出来的JAVA 对象,一般池对象都在这个区域活跃。

永久区
永久存储区是一个常驻内存区域,用于存放JDK自身所携带的Class, Interface的元数据,也就是说它存储的是运行环境必须的类信息,被装载进此区域的数据是不会被垃圾回收器回收掉的,关闭JVM才会释放此区域所占用的内存。如果出现java. lang. OutOfMemoryError: PermGen space, 说明是Java虛拟机对永久代Perm内存设置不够。
原因有二:
a.程序启动需 要加载大量的第三方jar包。例如:在-一个Tomcat 下部署了太多的应用。
b.大量动态 反射生成的类不断被加载,最终导致Perm区被占满。
说明:
Jdk1. 6及之前:常量池分配在永久代。
Jdk1.7:有,但已经逐步“去永久代”
Jdkl. 8及之后:无(java. lang. OutOfMemoryError: PermGen space, 这种错误将不会出现在JDK1.8中)。

方法区
方法区(Method Area) 与Java堆一样,是所有线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。Java虚拟机规范把方法区描述为堆的一个逻辑部分,但方法区有一个别名叫做非堆(Non Heap),目的是与Java堆区分开来。

类加载器的分类
Java提供以下三种系统类加载器:
1、启动类加载器( Bootstrap ClassLoader引导类加载器)
这个类负责将放在<JAVA_ HOME>\ib目录下,并且是虚拟机识别的(仅仅按照文件名识别,名字不符合的类库即使在lib目录下也不会被加载)类库加载到虚拟机内存中。启动类加载器无法被Java程序直接引用,如果要把加载请求委派给启动类加载器,那就直接用null代替即可。

2、扩展类加载器(Extension ClassLoader)
负责加载<JAVA_ HOME>\Vib\ext目录下的类库

3、应用程序类加载器(Application ClassLoader) 也称为系统类加载器
负责加载用户类路径上所指定的类库,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。通俗的来讲:一 般情况下,我们自己写的类是由这个加载器加载。

类加载的过程
JVM类加载工作原理:就是把类的class文件加载到内存中,并对数据进行校验、转换解析和初始化,最终形成被虚拟机
使用的java类型。
类加载的生命周期包括以下几个部分: 加载(Loading)、验证(Verification) 、准备( Preparation)、解析
( Resolution)、初始化(Initialization) 、使用(Using) 、卸载( Unloading) ,其中验证、准备、解析三个部分统称链接。

小结:

栈: 一个线程,对应一个虚拟机栈,一个方法对应一个栈帧,栈帧里面有存放局部变量表,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象),对象都存放在堆区中
堆: 堆是被所有线程共享的一块内存区域,在虚拟机启动时创建,几乎所有的对象实例都在这里分配内存,堆中存在字符串常量池
方法区: 所有线程共享的内存区域,存储已被虚拟机加载的类信息、常量、静态变量
程序计数器: 每个线程在运行期都有独立的程序计数器
本地方法栈: 为虚拟机使用到的Native方法服务

编写的java文件通过javac编译后生成class类文件,如果要去运行这个程序,首先虚拟机里面会有一个类加载器(Class loader),会把这个类加载到虚拟机(jvm)里面,存放在方法区(Method Area)里面,去使用new的时候,去静态方法区获取类信息,通过类信息去创建对象,在堆中生成内存地址。