码迷,mamicode.com
首页 > 其他好文 > 详细

JVM学习笔记

时间:2016-08-12 00:40:34      阅读:165      评论:0      收藏:0      [点我收藏+]

标签:

看的《深入理解Java虚拟机》

 


---------------Java内存区域-------------

一。运行时数据区域

1.Java虚拟机管理的内存包括的运行时数据区:程序计数器Program Couter Register,虚拟机栈VM Stack,本地方法栈Native Method Stack,堆Heap,方法区Method Area。
2.程序计数器:当前线程所执行的字节码的行号指示器。字节码解释器工作时通过改变计数器的值来选取下一条需要执行的字节码指令,分之、循环、跳转、异常处理、线程恢复等基础功能都需要依赖计数器完成。每条线程都有独立的程序计数器,各条线程之间的计数器互不影响,独立存储,这类内存区域为“线程私有”内存。若线程执行Java方法,计数器记录的是正在执行的虚拟机字节码地址,若是Native方法,计数器值为空。程序计数器的内存区域是唯一在JVM规范中没有规定任何OutOfMemoryError情况的区域。
3.Java虚拟机栈:线程私有的,生命周期与线程相同。VM栈是Java方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧Stack Frame,用于存储局部变量表、操作栈、动态链接、方法出口等信息。每个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈从入栈到出栈的过程。局部变量表存放了编译器可知的各种基本数据、对象引用和returnAddress类型,所需的内存空间在编译期间完成分配,进入方法时,方法需要的帧中局部变量空间Slot是完全确定的不会改变。如果线程请求的栈深度大于VM允许,抛出StackOverflowError异常;如果VM栈动态扩展到无法申请到足够内存时抛出OutOfMemoryError异常。
4.本地方法栈:与虚拟机栈作用类似,区别是使用的是Native方法服务(Sun HotSpot虚拟机将两者合二为一)。同样抛出StackOverflowError和OutOfMemoryError异常。
5.Java堆:存放对象实例,几乎所有对象实例都在堆上分配。是JVM内存中最大一块,是所有线程共享的内存区域,在JVM启动时创建。堆是垃圾收集的主要区域,由分代收集算法,堆分为新生代和老年代。再细致分为Eden空间、From Survivor空间、To Survivor空间。从内存分配角度看,线程共享的堆总可划分多个线程私有的分配缓冲区TLAB。无论如何划分,存储的仍然是对象实例,划分目的是回收和分配内存。堆可以处于物理上不连续的内存空间,大小可扩展(通过-XmxJVM最大允许分配堆内存,-Xms堆的最小值,-Xmn年轻代大小)若堆中没有内存完成实例分配且堆无法扩展时,抛出OutOfMemoryError异常。
6.方法区:线程共享的内存区域,存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。方法区被称为永久代Permanent Generation。方法区垃圾回收主要针对常量池的回收和对类型的卸载。方法区无法满足内存分配需求时将抛出OutOfMemoryError。
7.运行时常量池:方法区的一部分,用于存放编译器的字面量和符号引用,将在类加载后放入。具备动态性,运行期间可放新常量。
8.class文件内容:类的版本、字段、方法、接口等描述信息和常量池。
9.直接内存:不是JVM数据区内容。eg.NIO类使用Native函数库直接分配堆外内存。

二。对象访问

10.涉及栈、堆、方法区上重要内存区域的关联关系。
11.eg.Object obj=new Object(); :“Object obj”反映到栈的本地变量表中作为一个reference类型数据出现。“new Object()”反映到堆中,形成存储了Object类型所有实例数据值的结构化内存,堆中还包括能查找到此对象类型数据(如对象类型、父类、实现接口、方法等)的地址信息,这些类型数据则存储在方法区中。
12.reference类型规定了一个指向对象的引用,访问方式有两种:使用句柄和直接指针。句柄:堆中划分出一块内存作为句柄池,reference存储的是对象的句柄信息,句柄中包含了对象实例数据和类型数据各自的具体地址信息。直接指针:reference直接存储对象地址。(Sun HotSpot选用直接指针)

---------------垃圾收集器与内存分配策略-------------

一。垃圾收集条件判断

13.简介:Java垃圾回收机制用于在空闲时间以不定时的方式动态回收无任何引用的对象占据的内存空间(注意:垃圾回收回收的是无任何引用的对象占据的内存空间而不是对象本身)。程序计数器、虚拟机栈、本地方法栈三区域随线程而生而灭,内存分配和回收具备确定性,方法结束或线程结束时内存自然回收。而Java堆和方法区则不一样,只有在程序处于运行期间时才能知道创建的对象,这部分内存的分配和回收是动态的,垃圾收集器关注的是这部分内存。
14.判断对象不再被使用:1.引用计数算法(微软的COM技术、Python语言使用):引用则计数器+1,引用失效-1,计数器为0不再被使用。但是,难以解决对象之间的相互引用问题。2.根搜索算法(Java、C#使用)(第一次标记):通过一系列名为“GC Roots”的对象作为起始点,从这些节点开始向下搜索走过的路径,成为引用链Reference Chain,当GC Roots没有引用链相连即GC Roots到某对象不可达则该对象不可用即可回收。
15.Java中可作为GC Roots的对象包括:栈帧中本地变量表中引用的对象,方法区中类静态属性引用的对象,方法区中的常态引用的对象,本地方法栈中Native方法引用的对象。
16.JDK1.2前对引用的定义:如果Reference类型数据中存储的数值代表另一块内存的起始地址,就称这块内存代表着一个引用。
17.JDK1.2后引用概念补充:将引用分为强引用Strong,软引用Soft,弱引用Weak,虚引用Phantom,引用强度依次减弱。
18.强引用:程序代码中普遍存在的。eg.“Object obj=new Object()”,只要强引用存在,垃圾收集器不会回收被引用的对象。
19.软引用:还有用但非必须的对象。软引用着的对象,在系统发生内存溢出异常之前,会被列进回收范围之中。提供了SoftReference类来实现软引用。
20.弱引用:描述非必须对象,强度比软引用更弱,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。垃圾收集器工作时,无论内存是否足够都会回收只被弱引用关联的对象。提供了WeakRererence类实现弱引用。
21.虚引用:最弱的引用关系,一个对象是否有虚引用的存在都不会对生存时间构成影响,无法通过虚引用获得对象实例。设置虚引用关联的目的是在该对象呗收集器回收时收到系统通知。提供了PhantomReference类实现虚引用。
22.垃圾收集器的执行逻辑:先是两次标记:第一次,通过根搜素;第二次,此对象是否有必要执行finalize()方法,加入待删除的对象队列,垃圾收集器对待删除队列再次标记。然后垃圾收集器把两次标记的对象回收了。
23.回收方法区(永久代):永久代的垃圾收集主要回收两部分内容:废弃常量和无用的类。废弃常量的回收与根搜索方法同。类则需要满足以下三个条件才可判为无用的类:该类所有实例已被回收即堆中不存在该类任何实例;加载该类的ClassLoader已经被回收;该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。JVM仅仅是可以回收满足上述3个条件的无用类,而非和对象一样,不使用了必然回收。是否回收,HopSpot虚拟机提供了-Xnoclassgc参数进行控制

二。垃圾回收算法

24.标记-清除算法Mark-Sweep:内容顾名思义,是最基础的收集算法。缺点:效率不高;标记清除后产生大量不连续的内存碎片,当需要分配较大对象是不得不提前触发垃圾收集动作获得足够连续内存。
25.复制算法Copying:内存分为容量大小相等两块,一块内存用完了就将存活的对象复制到另外一块,然后将已使用过的内存空间清理。缺点:内存缩小为原来的一半。现在的商业虚拟机都采用复制算法回收新生代,且将内存分配改进为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中的一块Survivor空间。HotSpot虚拟机默认Eden和Survivor大小比例是8:1。
26.标记-整理算法:针对老年代,标记过程仍顾名思义,后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象向一端移动,然后直接清理掉端边界以外的内存。
27.分代收集算法:根据对象的存活周期不同将内存划分为几块。把Java堆分为新生代和老年代,新生代中每次垃圾收集只有少量存活,选用复制算法,老年代因为对象存活率高,使用标记-清理或标记-整理算法。

三。垃圾收集器(-XX:+UseXxxGC使用)

28.概论:收集算法是内存回收的方法论,垃圾收集器是内存回收的具体实现。有7种作用于不同分代的收集器。
29.Serial收集器(串行GC):Client模式下默认的新生代收集器。单线程,暂停其他所有的工作线程,能与CMS收集器配合。外号“Stop the World”,进行垃圾收集时必须暂停其他所有的工作线程。可用的控制参数eg.-XX:SurvivorRatio生存者比率即新生代中Eden区与Survivor区比值、-XX:PretenureSizeThreshold新生代晋升老年代对象大小、-XX:HandlePromotionFailure是否允许新生代担保。
30.ParNew收集器(并行GC):Serial收集器的多线程版本。Server模式下虚拟机首选的新生代收集器,因为除Serial外唯一能与CMS收集器配合工作。使用-XX:ParallelGCThreads参数限制垃圾收集的线程数。
31.区别概念:并行和并发。并行:多条垃圾收集线程并行工作,但用户线程仍处于等待状态。并发:用户线程与垃圾收集线程同时执行(可交替执行),用户线程继续执行,而垃圾收集程序运行于另一个CPU上。
32.Parallel Scavenge收集器(并行GC):新生代收集器,使用复制算法,并行多线程。外号:“吞吐量优先收集器”目标是达到可控制的吞吐量Throughput()吞吐量是CPU用于运行用户代码的时间与CPU总消耗时间的比值,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间))。提供了-XX:MaxGCPauseMillis控制最大垃圾收集停顿时间、-XX:GCTimeRatio设置吞吐量大小。
33.Parallel Scavenge收集器的GC自适应调节策略:设置-XX:+UseAdaptiveSizePolicy,不许指定-Xmn、Eden与Survivor区比例等细节参数,JVM根据系统运行情况收集性能监控信息,动态调节参数以提供最合适的停顿时间或最大吞吐量。自适应调节策略也是Parallel Scavenge收集器与ParNew收集器重要区别。
34.Serial Old收集器(串行GC):单线程收集器,使用“标记-整理”算法,是Serial收集器老年代版本。
35.Parallel Old收集器(并行GC):使用多线程和“标记-整理”算法。在注重吞吐量及CPU资源敏感的场合,优先考虑Parallel Scavenge加Parallel Old收集器。
36.CMS(Concurrent Mark Sweep)收集器(并发GC):以获取最短回收停顿时间为目标的基于“标记-清除”算法的收集器(重视服务的响应速度)。分为四步标记:初始标记,并发标记,重新标记,并发清除。总体说,CMS收集器的内存回收过程是与用户线程一起并发执行。三个缺点:CMS收集器对CPU资源敏感;无法处理浮动垃圾;产生大量空间碎片。
37.G1收集器(并发GC):两方面改进CMS:基于“标记-整理”算法,不会产生空间碎片;精确控制停顿。G1收集器可以实现在基本不牺牲吞吐量的前提下完成低停顿的内存回收。之前收集器收集范围是整个新生或老年代,而G1将堆划分为多个大小固定的独立区域Region,根据垃圾堆积程度在后台维护优先列表,每次优先收集垃圾最多区域(Garbage First)。
38.总结分类:Young Generation:Serial、ParNew、ParallelScavenge;使用复制算法,分为Eden和Survivor。Tenured Generation:Serial Old、Parallel Old、CMS;使用“标记-清除”或“标记-整理”算法。Region:G1,采用“标记-整理”算法
39.配合使用:除Serial+Serial Old和Parallel Scavenge+Parallel Old 可配合外,还有Serial+CMS,ParNew+CMS,三种年轻代都可+Serial Old。注意:Parallel Scavenge不可搭配CMS。

四。内存分配与回收策略

40.概论:Java技术体系提倡的自动内存管理可归结为自动解决两个问题:给对象分配内存及回收分配给对象的内存。
41.大体:对象优先在Eden分配;大对象直接进入老年代(-XX:PretenureSizeThreshold参数令大于这个设置值对象直接进入老年代分配,避免在Eden及Survivor去大量内存拷贝);长期存活的对象进入老年代(-XX:MaxTenuringThreshold设置晋升老年代的年龄阈值);动态对象年龄判定(如果在Survivor空间中相同年龄所有对象总和大于Survivor空间的一半,年龄大于或等于该年龄的对象可直接进入老年代);空间分配担保(老年代进行分配担保,让Survivor无法容纳的对象直接进入老年代)。

---------------类文件结构-------------

42.概述:Class文件格式采用类似于C语言结构体的伪结构存储,只有两种数据结构:基本数据类型的:无符号数;多个无符号数或其他表作为数据项构成的复合数据结构:表("_info"结尾)。整个Class文件本质上就是一张表。
43.常量池:Class文件结构中与其他项目关联最多的数据类型,也是Class文件空间占用最大的数据项目,是在Class文件中第一个出现的表类型数据项目。
44.访问标志:常量池结束后紧接着2个字节代表访问标志(access_flags),用于识别类或接口的访问信息。eg.Class是类或接口,是否定义public/abstrac/final等。
45.类索引、父类索引与接口索引集合;字段表集合;方法表集合;属性表集合。

---------------虚拟机类加载机制-------------

46.概述:类从被加载到JVM到卸载出内存为止,生命周期按顺序包括了:加载Loading、验证Verification、准备Preparation、解析Resolution、初始化Initialization、使用Using、卸载Unloading七个阶段。其中验证、准备和解析三部分被统称为连接Linking。
47.补充:解析阶段可能在初始化之后开始,因为Java语言的运行时绑定(动态绑定)。开始顺序是确定的,但所有阶段通常是互相交叉的进行。
48.开始类加载:JVM具体实现来自由把握。
49.必须立即对类初始化(在加载验证准备后):类进行主动引用的四种情况。1.遇到new、getstatic、putstatic、invokestatic这4条字节码指令时。eg.使用new实例化对象,读取或设置类的静态字段(final修饰的,已在编译器把结果放入常量池的静态字段除外),以及调用类的静态方法。2.使用java.lang.reflect包的方法进行反射调用的时候。3.当初始化一个类发现其父类还没进行过初始化,则会触发父类的初始化。4.JVM启动时用户执行的主类(包含main方法的类)。
50.不触发初始化:被动引用eg(P173开始):1.通过子类引用父类的静态字段。

JVM学习笔记

标签:

原文地址:http://www.cnblogs.com/roren/p/5763037.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!