JVM重要知识点整理和学习

     JVM是虚拟机,也是一种规范,他遵循着冯·诺依曼体系结构的设计原理。冯·诺依曼体系结构中,指出计算机处理的数据和指令都是二进制数,采用存储程序方式不加区分的存储在同一个存储器里,而且顺序执行,指令由操做码和地址码组成,操做码决定了操做类型和所操做的数的数字类型,地址码则指出地址码和操做数。从dos到window8,从unix到ubuntu和CentOS,还有MAC OS等等,不一样的操做系统指令集以及数据结构都有着差别,而JVM经过在操做系统上创建虚拟机,本身定义出来的一套统一的数据结构和操做指令,把同一套语言翻译给各大主流的操做系统,实现了跨平台运行,能够说JVM是java的核心,是java能够一次编译处处运行的本质所在。 javascript

我研究学习了JVM的组成和运行原理,JVM的统一数据格式规范、字节码文件结构,JVM关于内存的管理。 html

1、JVM的组成和运行原理

JVM的毕竟是个虚拟机,是一种规范,虽然说符合冯诺依曼的计算机设计理念,可是他并非实体计算机,因此他的组成也不是什么存储器,控制器,运算器,输入输出设备。在我看来,JVM放在运行在真实的操做系统中表现的更像应用或者说是进程,他的组成能够理解为JVM这个进程有哪些功能模块,而这些功能模块的运做能够看作是JVM的运行原理。JVM有多种实现,例如Oracle的JVM,HP的JVM和IBM的JVM等,而在本文中研究学习的则是使用最普遍的Oracle的HotSpot JVM。 java

 1.JVM在JDK中的位置。 程序员

JDK是java开发的必备工具箱,JDK其中有一部分是JRE,JRE是JAVA运行环境,JVM则是JRE最核心的部分。我从oracle.com截取了一张关于JDK Standard Edtion的组成图, 算法

JVM的相关知识整理和学习

从最底层的位置能够看出来JVM有多重要,而实际项目中JAVA应用的性能优化,OOM等异常的处理最终都得从JVM这儿来解决。HotSpot是Oracle关于JVM的商标,区别于IBM,HP等厂商开发的JVM。Java HotSpot Client VM和Java HotSpot Server VM是JDK关于JVM的两种不一样的实现,前者能够减小启动时间和内存占用,然后者则提供更加优秀的程序运行速度(参考自:http://docs.oracle.com/javase/8/docs/technotes/guides/vm/index.html ,该文档有关于各个版本的JVM的介绍)。在命令行,经过java -version能够查看关于当前机器JVM的信息,下面是我在Win8系统上执行命令的截图, ubuntu

JVM的相关知识整理和学习

能够看出我装的是build 20.13-b02版本,HotSpot 类型Server模式的JVM。 windows

2.JVM的组成 数组

JVM由4大部分组成:ClassLoader,Runtime Data Area,Execution Engine,Native Interface。 性能优化

我从CSDN找了一张描述JVM大体结构的图: 网络

JVM的相关知识整理和学习

2.1.ClassLoader是负责加载class文件,class文件在文件开头有特定的文件标示,而且ClassLoader只负责class文件的加载,至于它是否能够运行,则由Execution Engine决定。

2.2.Native Interface是负责调用本地接口的。他的做用是调用不一样语言的接口给JAVA用,他会在Native Method Stack中记录对应的本地方法,而后调用该方法时就经过Execution Engine加载对应的本地lib。本来多于用一些专业领域,如JAVA驱动,地图制做引擎等,如今关于这种本地方法接口的调用已经被相似于Socket通讯,WebService等方式取代。

2.3.Execution Engine是执行引擎,也叫Interpreter。Class文件被加载后,会把指令和数据信息放入内存中,Execution Engine则负责把这些命令解释给操做系统。

2.4.Runtime Data Area则是存放数据的,分为五部分:Stack,Heap,Method Area,PC Register,Native Method Stack。几乎全部的关于java内存方面的问题,都是集中在这块。下图是javapapers.com上关于Run-time Data Areas的描述:

JVM的相关知识整理和学习

能够看出它把Method Area化为了Heap的一部分,javapapers.com中认为Method Area是Heap的逻辑区域,但这取决于JVM的实现者,而HotSpot JVM中把Method Area划分为非堆内存,显然是不包含在Heap中的。下图是javacodegeeks.com中,2014年9月刊出的一片博文中关于Runtime Data Area的划分,其中指出,NonHeap包含PermGen和Code Cache,PermGen包含Method Area,并且PermGen在JAVA SE 8中已经再也不用了。查阅资料(https://abhirockzz.wordpress.com/2014/03/18/java-se-8-is-knocking-are-you-there/)得知,java8中PermGen已经从JVM中移除并被MetaSpace取代,java8中也不会见到OOM:PermGen Space的异常。目前Runtime Data Area能够用下图描述它的组成:

JVM的相关知识整理和学习

2.4.1.Stack是java栈内存,它等价于C语言中的栈,栈的内存地址是不连续的,每一个线程都拥有本身的栈。栈里面存储着的是StackFrame,在《JVM Specification》中文版中被译做java虚拟机框架,也叫作栈帧。StackFrame包含三类信息:局部变量,执行环境,操做数栈。局部变量用来存储一个类的方法中所用到的局部变量。执行环境用于保存解析器对于java字节码进行解释过程当中须要的信息,包括:上次调用的方法、局部变量指针和操做数栈的栈顶和栈底指针。操做数栈用于存储运算所须要的操做数和结果。StackFrame在方法被调用时建立,在某个线程中,某个时间点上,只有一个框架是活跃的,该框架被称为Current Frame,而框架中的方法被称为Current Method,其中定义的类为Current Class。局部变量和操做数栈上的操做老是引用当前框架。当Stack Frame中方法被执行完以后,或者调用别的StackFrame中的方法时,则当前栈变为另一个StackFrame。Stack的大小是由两种类型,固定和动态的,动态类型的栈能够按照线程的须要分配。 下面两张图是关于栈之间关系以及栈和非堆内存的关系基本描述(来自http://www.programering.com/a/MzM3QzNwATA.html):

JVM的相关知识整理和学习 JVM的相关知识整理和学习

2.4.2.Heap是用来存放对象信息的,和Stack不一样,Stack表明着一种运行时的状态。换句话说,栈是运行时单位,解决程序该如何执行的问题,而堆是存储的单位,解决数据存储的问题。Heap是伴随着JVM的启动而建立,负责存储全部对象实例和数组的。堆的存储空间和栈同样是不须要连续的,它分为Young Generation和Old Generation(也叫Tenured Generation)两大部分。Young Generation分为Eden和Survivor,Survivor又分为From Space和 ToSpace。

和Heap常常一块儿说起的概念是PermanentSpace,它是用来加载类对象的专门的内存区,是非堆内存,和Heap一块儿组成JAVA内存,它包含MethodArea区(在没有CodeCache的HotSpotJVM实现里,则MethodArea就至关于GenerationSpace)。在JVM初始化的时候,咱们能够经过参数来分别指定,PermanentSpace的大小、堆的大小、以及Young Generation和Old Generation的比值、Eden区和From Space的比值,从而来细粒度的适应不一样JAVA应用的内存需求。

2.4.3.PC Register是程序计数寄存器,每一个JAVA线程都有一个单独的PC Register,他是一个指针,由Execution Engine读取下一条指令。若是该线程正在执行java方法,则PC Register存储的是 正在被执行的指令的地址,若是是本地方法,PC Register的值没有定义。PC寄存器很是小,只占用一个字宽,能够持有一个returnAdress或者特定平台的一个指针。

2.4.4.Method Area在HotSpot JVM的实现中属于非堆区,非堆区包括两部分:Permanet Generation和Code Cache,而Method Area属于Permanert Generation的一部分。Permanent Generation用来存储类信息,好比说:class definitions,structures,methods, field, method (data and code) 和 constants。Code Cache用来存储Compiled Code,即编译好的本地代码,在HotSpot JVM中经过JIT(Just In Time) Compiler生成,JIT是即时编译器,他是为了提升指令的执行效率,把字节码文件编译成本地机器代码,以下图:

JVM的相关知识整理和学习

引用一个经典的案例来理解Stack,Heap和Method Area的划分,就是Sring a=”xx”;Stirng b=”xx”,问是否a==b? 首先==符号是用来判断两个对象的引用地址是否相同,而在上面的题目中,a和b按理来讲申请的是Stack中不一样的地址,可是他们指向Method Area中Runtime Constant Pool的同一个地址,按照网上的解释,在a赋值为“xx”时,会在Runtime Contant Pool中生成一个String Constant,当b也赋值为“xx”时,那么会在常量池中查看是否存在值为“xx”的常量,存在的话,则把b的指针也指向“xx”的地址,而不是新生成一个String Constant。我查阅了网络上你们关于String Constant的存储的说说法,存在略微差异的是,它存储在哪里,有人说Heap中会分配出一个常量池,用来存储常量,全部线程共享它。而有人说常量池是Method Area的一部分,而Method Area属于非堆内存,那怎么能说常量池存在于堆中?

我认为,其实两种理解都没错。Method Area的确从逻辑上讲能够是Heap的一部分,在某些JVM实现里从堆上开辟一块存储空间来记录常量是符合JVM常量池设计目的的,因此前一种说法没问题。对于后一种说法,HotSpot JVM的实现中的确是把方法区划分为了非堆内存,意思就是它不在堆上。我在HotSpot JVM作了个简单的实验,定义多个常量以后,程序抛出OOM:PermGen Space异常,印证了JVM实现中常量池是在Permanent Space中的说法。可是,个人JDK版本是1.6的。查阅资料,JDK1.7中InternedStrings已经再也不存储在PermanentSpace中,而是放到了Heap中;JDK8中PermanentSpace已经被彻底移除,InternedStrings也被放到了MetaSpace中(若是出现内存溢出,会报OOM:MetaSpace,这里有个关于二者性能对比的文章:http://blog.csdn.net/zhyhang/article/details/17246223 )。 因此,仁者见仁,智者见智,一个馒头足以引起血案,就算是同一个商家的JVM,毕竟JDK版本在更新,或许正如StackOverFlow上大神们所说,对于理解JVM Runtime Data Area这一部分的划分逻辑,仍是去看对应版本的JDK源码比较靠谱,或者是参考不一样的版本JVM Specification( http://docs.oracle.com/javase/specs/ )。

2.4.5.Native Method Stack是供本地方法(非java)使用的栈。每一个线程持有一个Native Method Stack。

 3.JVM的运行原理简介

Java 程序被javac工具编译为.class字节码文件以后,咱们执行java命令,该class文件便被JVM的Class Loader加载,能够看出JVM的启动是经过JAVA Path下的java.exe或者java进行的。JVM的初始化、运行到结束大概包括这么几步:

调用操做系统API判断系统的CPU架构,根据对应CPU类型寻找位于JRE目录下的/lib/jvm.cfg文件,而后经过该配置文件找到对应的jvm.dll文件(若是咱们参数中有-server或者-client, 则加载对应参数所指定的jvm.dll,启动指定类型的JVM),初始化jvm.dll而且挂接到JNIENV结构的实例上,以后就能够经过JNIENV实例装载而且处理class文件了。class文件是字节码文件,它按照JVM的规范,定义了变量,方法等的详细信息,JVM管理而且分配对应的内存来执行程序,同时管理垃圾回收。直到程序结束,一种状况是JVM的全部非守护线程中止,一种状况是程序调用System.exit(),JVM的生命周期也结束。

关于JVM如何管理分配内存,我经过class文件和垃圾回收两部分进行了学习。

2、JVM的内存管理和垃圾回收

JVM中的内存管理主要是指JVM对于Heap的管理,这是由于Stack,PC Register和Native Method Stack都是和线程同样的生命周期,在线程结束时天然能够被再次使用。虽说,Stack的管理不是重点,可是也不是彻底不讲究的。

1.栈的管理

     JVM容许栈的大小是固定的或者是动态变化的。在Oracle的关于参数设置的官方文档中有关于Stack的设置(http://docs.oracle.com/cd/E13150_01/jrockit_jvm/jrockit/jrdocs/refman/optionX.html#wp1024112),是经过-Xss来设置其大小。关于Stack的默认大小对于不一样机器有不一样的大小,而且不一样厂商或者版本号的jvm的实现其大小也不一样,以下表是HotSpot的默认大小:

Platform
Default
Windows IA32
64 KB
Linux IA32
128 KB
Windows x86_64
128 KB
Linux x86_64
256 KB
Windows IA64
320 KB
Linux IA64
1024 KB (1 MB)
Solaris Sparc
512 KB

咱们通常经过减小常量,参数的个数来减小栈的增加,在程序设计时,咱们把一些常量定义到一个对象中,而后来引用他们能够体现这一点。另外,少用递归调用也能够减小栈的占用。    栈是不须要垃圾回收的,尽管说垃圾回收是java内存管理的一个很热的话题,栈中的对象若是用垃圾回收的观点来看,他永远是live状态,是能够reachable的,因此也不须要回收,他占有的空间随着Thread的结束而释放。(参考自:http://stackoverflow.com/questions/20030120/java-default-stack-size

关于栈通常会发生如下两种异常:

1.当线程中的计算所须要的栈超过所容许大小时,会抛出StackOverflowError。

2.当Java栈试图扩展时,没有足够的存储器来实现扩展,JVM会报OutOfMemoryError。    我针对栈进行了实验,因为递归的调用能够导致栈的引用增长,致使溢出,因此设计代码以下:

JVM的相关知识整理和学习

个人机器是x86_64系统,因此Stack的默认大小是128KB,上述程序在运行时会报错:

JVM的相关知识整理和学习

而当我在Eclipse中调整了-Xss参数到3M以后,该异常消失。

JVM的相关知识整理和学习

另外栈上有一点得注意的是,对于本地代码调用,可能会在栈中申请内存,好比C调用malloc(),而这种状况下,GC是管不着的,须要咱们在程序中,手动管理栈内存,使用free()方法释放内存。

 2.堆的管理

堆的管理要比栈管理复杂的多,我经过堆的各部分的做用、设置,以及各部分可能发生的异常,以及如何避免各部分异常进行了学习。

JVM的相关知识整理和学习

上图是 Heap和PermanentSapce的组合图,其中 Eden区里面存着是新生的对象,From Space和To Space中存放着是每次垃圾回收后存活下来的对象 ,因此每次垃圾回收后,Eden区会被清空。 存活下来的对象先是放到From Space,当From Space满了以后移动到To Space。当To Space满了以后移动到Old Space。Survivor的两个区是对称的,没前后关系,因此同一个区中可能同时存在从Eden复制过来 对象,和从前一个Survivor复制过来的对象,而复制到年老区的只有从第一个Survivor复制过来的对象。并且,Survivor区总有一个是空的。同时,根据程序须要,Survivor区是能够配置为多个的(多于两个),这样能够增长对象在年轻代中的存在时间,减小被放到年老代的可能。

Old Space中则存放生命周期比较长的对象,并且有些比较大的新生对象也放在Old Space中。

堆的大小经过-Xms和-Xmx来指定最小值和最大值,经过-Xmn来指定Young Generation的大小(一些老版本也用-XX:NewSize指定), 即上图中的Eden加FromSpace和ToSpace的总大小。而后经过-XX:NewRatio来指定Eden区的大小,在Xms和Xmx相等的状况下,该参数不须要设置。经过-XX:SurvivorRatio来设置Eden和一个Survivor区的比值。(参考自博文:http://www.cnblogs.com/redcreen/archive/2011/05/04/2037057.html

堆异常分为两种,一种是Out of Memory(OOM),一种是Memory Leak(ML)。Memory Leak最终将致使OOM。实际应用中表现为:从Console看,内存监控曲线一直在顶部,程序响应慢,从线程看,大部分的线程在进行GC,占用比较多的CPU,最终程序异常终止,报OOM。OOM发生的时间不定,有短的一个小时,有长的10天一个月的。关于异常的处理,肯定OOM/ML异常后,必定要注意保护现场,能够dump heap,若是没有现场则开启GCFlag收集垃圾回收日志,而后进行分析,肯定问题所在。若是问题不是ML的话,通常经过增长Heap,增长物理内存来解决问题,是的话,就修改程序逻辑。

3.垃圾回收

JVM中会在如下状况触发回收:对象没有被引用,做用域发生未捕捉异常,程序正常执行完毕,程序执行了System.exit(),程序发生意外终止。

JVM中标记垃圾使用的算法是一种根搜索算法。简单的说,就是从一个叫GC Roots的对象开始,向下搜索,若是一个对象不能达到GC Roots对象的时候,说明它能够被回收了。这种算法比一种叫作引用计数法的垃圾标记算法要好,由于它避免了当两个对象啊互相引用时没法被回收的现象。

JVM中对于被标记为垃圾的对象进行回收时又分为了一下3种算法:

1.标记清除算法,该算法是从根集合扫描整个空间,标记存活的对象,而后在扫描整个空间对没有被标记的对象进行回收,这种算法在存活对象较多时比较高效,但会产生内存碎片。

2.复制算法,该算法是从根集合扫描,并将存活的对象复制到新的空间,这种算法在存活对象少时比较高效。

3.标记整理算法,标记整理算法和标记清除算法同样都会扫描并标记存活对象,在回收未标记对象的同时会整理被标记的对象,解决了内存碎片的问题。

JVM中,不一样的 内存区域做用和性质不同,使用的垃圾回收算法也不同,因此JVM中又定义了几种不一样的垃圾回收器(图中连线表明两个回收器能够同时使用):

JVM的相关知识整理和学习

1.Serial GC。从名字上看,串行GC意味着是一种单线程的,因此它要求收集的时候全部的线程暂停。这对于高性能的应用是不合理的,因此串行GC通常用于Client模式的JVM中。

2.ParNew GC。是在SerialGC的基础上,增长了多线程机制。可是若是机器是单CPU的,这种收集器是比SerialGC效率低的。

3.Parrallel Scavenge GC。这种收集器又叫吞吐量优先收集器,而吞吐量=程序运行时间/(JVM执行回收的时间+程序运行时间),假设程序运行了100分钟,JVM的垃圾回收占用1分钟,那么吞吐量就是99%。Parallel Scavenge GC因为能够提供比较不错的吞吐量,因此被做为了server模式JVM的默认配置。

4.ParallelOld是老生代并行收集器的一种,使用了标记整理算法,是JDK1.6中引进的,在以前老生代只能使用串行回收收集器。

5.Serial Old是老生代client模式下的默认收集器,单线程执行,同时也做为CMS收集器失败后的备用收集器。

6.CMS又称响应时间优先回收器,使用标记清除算法。他的回收线程数为(CPU核心数+3)/4,因此当CPU核心数为2时比较高效些。CMS分为4个过程:初始标记、并发标记、从新标记、并发清除。

7.GarbageFirst(G1)。比较特殊的是G1回收器既能够回收Young Generation,也能够回收Tenured Generation。它是在JDK6的某个版本中才引入的,性能比较高,同时注意了吞吐量和响应时间。

对于垃圾收集器的组合使用能够经过下表中的参数指定:

JVM的相关知识整理和学习

默认的GC种类能够经过jvm.cfg或者经过jmap dump出heap来查看,通常咱们经过jstat -gcutil [pid] 1000能够查看每秒gc的大致状况,或者能够在启动参数中加入:-verbose:gc -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:./gc.log来记录GC日志

GC中有一种状况叫作Full GC,如下几种状况会触发Full GC:

1.Tenured Space空间不足以建立打的对象或者数组,会执行FullGC,而且当FullGC以后空间若是还不够,那么会OOM:java heap space。

2.Permanet Generation的大小不足,存放了太多的类信息,在非CMS状况下回触发FullGC。若是以后空间还不够,会OOM:PermGen space。

3.CMS GC时出现promotion failed和concurrent mode failure时,也会触发FullGC。promotion failed是在进行Minor GC时,survivor space放不下、对象只能放入旧生代,而此时旧生代也放不下形成的;concurrent mode failure是在执行CMS GC的过程当中同时有对象要放入旧生代,而此时旧生代空间不足形成的。

4.判断MinorGC后,要晋升到TenuredSpace的对象大小大于TenuredSpace的大小,也会触发FullGC。

能够看出,当FullGC频繁发生时,必定是内存出问题了。

3、JVM的数据格式规范和Class文件

1.数据类型规范

依据冯诺依曼的计算机理论,计算机最后处理的都是二进制的数,而JVM是怎么把java文件最后转化成了各个平台均可以识别的二进制呢?JVM本身定义了一个抽象的存储数据单位,叫作Word。一个字足够大以持有byte、char、short、int、float、reference或者returnAdress的一个值,两个字则足够持有更大的类型long、double。它一般是主机平台一个指针的大小,如32位的平台上,字是32位。

同时JVM中定义了它所支持的基本数据类型,包括两部分:数值类型和returnAddress类型。数值类型分为整形和浮点型。

整形:

byte
值是8位的有符号二进制补码整数
short
值是16位的有符号二进制补码整数
int
值是32位的有符号二进制补码整数
long
值是64位的有符号二进制补码整数
char
值是表示Unicode字符的16位无符号整数

浮点:

float
值是32位IEEE754 浮点数
double
值是64位IEEE754浮点数

returnAddress类型的值是Java虚拟机指令的操做码的指针。

对比java的基本数据类型,jvm的规范中没有boolean类型。这是由于jvm中堆boolean的操做是经过int类型来进行处理的,而boolean数组则是经过byte数组来进行处理。

至于String,咱们知道它存储在常量池中,但他不是基本数据类型,之因此能够存在常量池中,是由于这是JVM的一种规定。若是查看String源码,咱们就会发现,String其实就是一个基于基本数据类型char的数组。如图:

JVM的相关知识整理和学习

2.字节码文件

经过字节码文件的格式咱们能够看出jvm是如何规范数据类型的。下面是ClassFile的结构:

JVM的相关知识整理和学习

关于各个字段的定义(参考自JVM Specification 和 博文:http://www.cnblogs.com/zhuYears/archive/2012/02/07/2340347.html),

Magic

魔数,魔数的惟一做用是肯定这个文件是否为一个能被虚拟机所接受的Class文件。魔数值固定为0xCAFEBABE,不会改变。

minor_version、major_version:

分别为Class文件的副版本和主版本。它们共同构成了Class文件的格式版本号。不一样版本的虚拟机实现支持的Class文件版本号也相应不一样,高版本号的虚拟机能够支持低版本的Class文件,反之则不成立。

constant_pool_count:

常量池计数器,constant_pool_count的值等于constant_pool表中的成员数加1。

constant_pool[]:

常量池,constant_pool是一种表结构,它包含Class文件结构及其子结构中引用的全部字符串常量、类或接口名、字段名和其它常量。常量池不一样于其余,索引从1开始到constant_pool_count -1。

access_flags:

访问标志,access_flags是一种掩码标志,用于表示某个类或者接口的访问权限及基础属性。access_flags的取值范围和相应含义见下表:

JVM的相关知识整理和学习

this_class:

类索引,this_class的值必须是对constant_pool表中项目的一个有效索引值。constant_pool表在这个索引处的项必须为CONSTANT_Class_info类型常量,表示这个Class文件所定义的类或接口。

super_class:

父类索引,对于类来讲,super_class的值必须为0或者是对constant_pool表中项目的一个有效索引值。若是它的值不为0,那constant_pool表在这个索引处的项必须为CONSTANT_Class_info类型常量,表示这个Class文件所定义的类的直接父类。固然,若是某个类super_class的值是0,那么它一定是java.lang.Object类,由于只有它是没有父类的。

interfaces_count:

接口计数器,interfaces_count的值表示当前类或接口的直接父接口数量。

interfaces[]:

接口表,interfaces[]数组中的每一个成员的值必须是一个对constant_pool表中项目的一个有效索引值,它的长度为interfaces_count。每一个成员interfaces[i] 必须为CONSTANT_Class_info类型常量。

fields_count:

字段计数器,fields_count的值表示当前Class文件fields[]数组的成员个数。

fields[]:

字段表,fields[]数组中的每一个成员都必须是一个fields_info结构的数据项,用于表示当前类或接口中某个字段的完整描述。

methods_count:

方法计数器,methods_count的值表示当前Class文件methods[]数组的成员个数。

methods[]:

方法表,methods[]数组中的每一个成员都必须是一个method_info结构的数据项,用于表示当前类或接口中某个方法的完整描述。

attributes_count:

属性计数器,attributes_count的值表示当前Class文件attributes表的成员个数。

attributes[]:

属性表,attributes表的每一个项的值必须是attribute_info结构。

4、一个java类的实例分析

为了了解JVM的数据类型规范和内存分配的大致状况,我新建了MemeryTest.java:

JVM的相关知识整理和学习

编译为MemeryTest.class后,经过WinHex查看该文件,对应字节码文件各个部分不一样的定义,我了解了下面16进制数值的具体含义,尽管不清楚ClassLoader的具体实现逻辑,可是能够想象这样一个严谨格式的文件给JVM对于内存管理和执行程序提供了多大的帮助。

JVM的相关知识整理和学习

运行程序后,我在windows资源管理器中找到对应的进程ID.

JVM的相关知识整理和学习

而且在控制台经过jmap -heap 10016查看堆内存的使用状况:

JVM的相关知识整理和学习

输出结果中表示当前java进程启动的JVM是经过4个线程进行Parallel GC,堆的最小FreeRatio是40%,堆的最大FreeRatio是70%,堆的大小是4090M,新对象占用1.5M,Young Generation能够扩展到最大是1363M, Tenured Generation的大小是254.5M,以及NewRadio和SurvivorRadio中,下面更是具体给出了目前Young Generation中1.5M的划分状况,Eden占用1.0M,使用了5.4%,Space占了0.5M,使用了93%,To Space占了0.5M,使用了0%。

下面咱们经过jmap dump把heap的内容打印打文件中:

JVM的相关知识整理和学习

使用Eclipse的MAT插件打开对应的文件:

JVM的相关知识整理和学习

选择第一项内存泄露分析报告打开test.bin文件,展现出来的是MAT关于内存可能泄露的分析。

JVM的相关知识整理和学习

JVM的相关知识整理和学习

从结果来看,有3个地方可能存在内存泄露,他们占据了Heap的22.10%,13.78%,14.69%,若是内存泄露,这里通常会有一个比值很是高的对象。打开第一个Probem Suspect,结果以下:

JVM的相关知识整理和学习

ShallowHeap是对象自己占用的堆大小,不包含引用,RetainedHeap是对象所持有的Shallowheap的大小,包括本身ShallowHeap和能够引用的对象的ShallowHeap。垃圾回收的时候,若是一个对象再也不引用后被回收,那么他的RetainedHeap是能回收的内存总和。经过上图能够看出程序中并无什么内存泄露,能够放心了。若是还有什么不太肯定的对象,则能够经过多个时间点的HeapDumpFile来研究某个对象的变化状况。

5、小结

以上即是我最近几天对JVM相关资料的整理,主要围绕他的基本组成和运行原理等,内存管理,节本数据类型和字节码文件。JVM是一个很是优秀的JAVA程序,也是个不错的规范,此次整理学习让我对他有了更加清晰的认知,对Java语言的理解也更加加深。

此次学习过程,坚决了我对程序员发展的认知。知识必定要精,下一步我将边工做边仔细阅读Oracle的3个版本的《JVM Specification》,而且结合实践让本身的Java基础素养更上一个层次。(http://docs.oracle.com/javase/specs/)

分享到: 更多 5
继续浏览有关 JVM的文章
相关文章
相关标签/搜索