jvm的所有知识点

1.jvm的内存结构:

 程序计数器

 java虚拟机栈

 本地方法栈

 堆

 本地方法区

首先引入一幅图说明Java各对象的存放位置:

(1) 程序计数器:记录当前线程正在执行字节码指令的地址(如果正在执行的是java方法,该计数器记录的是正在执行

          的虚拟机字节码指令的地址;如果正在执行的是Native方法,该计数器的值为空(Undefined))。该区域是线程

          私有的,生命周期随着线程的创建而创建,随着线程的消亡而消亡。读取指令举例:如顺序、分支、循环、跳转、

          异常处理、线程恢复等

 (2)Java虚拟机栈:即我们所说的栈,线程私有,由栈帧组成。

          定义:虚拟机栈描述的是java方法执行的内存模型。每个方法执行时都会创建一个栈帧用于存储局部变量表,操作数栈、

                    动态链接、方法出口等信息,每个方法被调用直至调用完成的过程,就对应一个栈帧在虚拟机中从入栈到出栈的

                    过程。例如:函数1对应栈帧1、函数2对应栈帧2、函数3对应栈帧3、函数4对应栈帧4;函数1调用函数2、函数2

                    调用函数3、函数3调用函数4;当函数1被调用时时栈帧1入栈、函数2被调用时栈帧2入栈、函数3被调用时栈帧

                    3入栈、函数4被调用时栈帧4入栈;当函数4执行完栈帧4出栈并执行函数3;函数3执行完栈帧3出栈并执行函数2;

                    函数2执行完栈帧2出栈并执行函数1;函数1执行完栈帧1出栈。当前正在执行的函数锁对应的帧一定是位于栈

                    底的帧。

        栈帧包括:局部变量表、操作数栈、常量池、动态链接、方法出口信息等

        局部变量表:局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。在Java程序被编译

                             为Class文件时,就在方法的Code属性的max_locals数据项中确定了方法所需要分配的最大局部变量表的

                             容量。

 动态链接:即用到某个类才加载到内存

 静态链接:所有类都加载无论是否用到

 操作数栈:Java 虚拟机的指令是从操作数栈中而不是从寄存器中取得操作数的,因此它的运行方式是基于栈的而

                 不是基于寄存器的。如下例:

                比如 a = 1 + 2 

                iload_0 //将 1 压入操作数栈 

                iload_1 //将 2 压入操作数栈 

                iadd //从操作数栈中弹出 1、2,将算出的值 3 压入操作数栈 

                istore_2 //把 3 从操作数栈中弹出,保存到本地变量区 

(3)本地方法栈:和虚拟机栈类似,区别在与虚拟机栈为虚拟机执行java方法时服务,而本地方法栈则为虚拟机使

                     用到的Native方法服务,本地方法被执行的时候,在本地方法会创建一个栈帧,用于存放局部变

                     量表、操作数栈、动态链接、出口信息。

(4)堆:此内存区域的唯一目的就是存放对象实例,Java虚拟机规范中的描述是:“所有对象实例以及数组都要在

                堆上分配”,但是随着JIT编译器的发展与逃逸分析技术逐渐成熟,栈上分配、标量替换优化技术将会导致

                 一些微妙的变化发生,所有对象都分配在堆上也渐渐变得不是那么绝对了。

                 该区域是线程共享的,垃圾回收的主要场所,Java虚拟机规范规范规定:Java堆可以处于物理上不连续的

                 内存空间中,只要逻辑上连续即可。

                 如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。

(5)方法区:堆的一个逻辑部分,线程共享,存放已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译

              后的代码等。对于 HotSpot该区域叫做永久代。

                     及时编译器:Java程序最初是通过解释器(Interpreter)进行解释执行的,当虚拟机发现某个方法或代

                                          码块的运行特别频繁时,就会把这些代码认定为“热点代码”。为了提高热点代码的执行效

                                          率,在运行时,虚拟机将会把这些代码编译成与本地平台相关的机器码,并进行各种层次

                                          的优化,完成这个任务的编译器称为即时编译器(Just In Time Compiler,下文统称JIT编

                                         译器)

                     编译和解释:编译型语言在编译过程中生成目标平台的指令,解释型语言在运行过程中才生成目标平台

                                          的指令。

        (6)运行时常量池:略。

        (7)直接内存:略。

2.jvm的垃圾回收算法

(1)引用计数法:堆中的每一个对象都会有一个引用计数值,该对象每增加一个引用的时候,该计

         数加一,反之则减一。在执行垃圾回收时,那些引用计数值为0的对象将会被回收。这种方法的

         一个致命缺陷是无法处理循环带来的引用问题,即当两个对象互相引用时,它们的引用计数值永

        远不可能为0。例如:

         

/** 
 * 执行后,objA和objB会不会被GC呢? 
 */  
public class ReferenceCountingGC {  
    public Object instance = null;  
  
    private static final int _1MB = 1024 * 1024;  
  
    /** 
     * 这个成员属性的唯一意义就是占点内存,以便能在GC日志中看清楚是否被回收过 
     */  
    private byte[] bigSize = new byte[2 * _1MB];  
  
    public static void main(String[] args) {  
        ReferenceCountingGC objA = new ReferenceCountingGC();  
        ReferenceCountingGC objB = new ReferenceCountingGC();  
        objA.instance = objB;  
        objB.instance = objA;  
  
        objA = null;  
        objB = null;  
  
        //假设在这行发生了GC,objA和ojbB是否被回收  
        System.gc();  
    

             在testGC()方法中,对象objA和objB都有字段instance,赋值令objA.instance=objB及objB.instance=objA,

             除此之外这两个对象再无任何引用,实际上这两个对象都已经不能再被访问,但是它们因为相互引用着对象方,

             异常它们的引用计数都不为0,于是引用计数算法无法通知GC收集器回收它们

(2)标记清除算法:分为两个阶段,标注和清除。标记阶段标记出所有需要回收的对象,清除阶段回收被标记

         的对象所占用的空间,该算法最大的问题是内存碎片产生严重

(3)复制算法:为了解决Mark-Sweep算法内存碎片化的缺陷而被提出的算法。按内存容量将内存划分为等大

         小的两块。每次只使用其中一块,当这一块内存满后将尚存活的对象复制到另一块上去,把已使用的内存

         清掉。这种算法虽然实现简单,内存效率高,不易产生碎片,但是最大的问题是可用内存被压缩到了原本

         的一半。且存活对象增多的话,Copying算法的效率会大大降低。

(4)标记压缩法:标记阶段和标记算法相同,标记后不是清理对象而是将存活的对象移动到内存的一端,然后

        清除边界外的对象。

(5)分代算法:分代收集法是目前大部分JVM所采用的方法,其核心思想是根据对象存活的不同

         生命周期将内存划分为不同的域,一般情况下将GC堆划分为老生代(Tenured/Old Generation)

         和新生代(Young Generation)。老生代的特点是每次垃圾回收时只有少量对象需要被回收,新

        生代的特点是每次垃圾回收时都有大量垃圾需要被回收,因此可以根据不同区域选择不同的算法。

        新生代采用复制算法,老年代采用标记压缩算法。

(6)分区算法:将整个内存分为N个小的独立空间,每个小空间都可以独立使用,这样细粒度控制一次

        回收多少个小空间和哪些个小空间,而不是对整个空间进行GC,从而提升性能

3.垃圾回收器

(1)串行回收器:使用单线程进行垃圾回收的回收器,每次回收时,串行回收器只有一个工作线程,对于并行能力

         较弱的服务器,串行回收器会有更好的表现,串行回收器可以在新生代和老年代使用,根据作用于不同的堆空

         间有新生代串行回收器和老年代串行回收器

(2)并行回收器:它在串行回收器的基础上做了改进,它可以使用多个线程同时进行垃圾回收,对于计算能力较强

        的服务器而言可以有效的缩短垃圾回收的时间

        ParNew回收器:是一个工作在新生代的垃圾回收器,它只是简单的将串行回收器多线程化,它的回收策略和算

        法和串行回收器一样。

        使用-XX:+UseParNewGC开启并行回收器,此时老年代默认使用串行回收器

        ParnNew回收器使用“-XX:ParallelGCThreads参数指定”工作线程数量,一般最好和计算机的CPU数量相

        当。

        ParallelGC回收器:是工作在新生代的垃圾回收器,使用了复制算法的收集器,也是多线程独占(独占回

        收器:GC来的时候应用停顿只执行GC)形式的收集器非常关注系统的吞吐量,如下两个参数控制系统的

        吞吐量:

        -XX:MaxGCPauseMills:设置最大垃圾回收停顿时间,将此值减少可以减少GC垃圾会减少垃圾收集的停顿

        时间,但是会导致GC频繁,重而增加了垃圾回收的总时间,降低了吞吐量,所以需要根据实际情况设置该

        值。

       -XX:GCTimeRatio:设置吞吐量大小,他是一个0到100之间的整数,默认情况下他的取值是99,那么系统

       将花费不超过1/(1+n)的时间用于垃圾回收,也就是1/(1+99)=1%的时间

       -XX:+UseAdaptiveSizePollcy:打开自适应模式,在这种模式下新生代的大小、eden、from/to的比例,

       以及晋升到老年代的对象年龄参数会自动调整,以达到堆大小、吞吐量和停顿时间之间的平衡点。

       ParallelOldGC:是一种多线程关注吞吐量的回收器,它使用了标记压缩算法进行实现。

       -XX:+UseParallelOldGC进行设置使用

       -XX:+ParallelGCThreads:设置垃圾回收时的线程数量。

(3)CMS回收器:它使用的是标记清除法,主要关注的是系统的停顿时间。CMS并不是独占的回收器,也就是

         说CMS回收的过程中,应用程序仍然在不停的工作,又会有新的垃圾不断产生,所以在使用CMS过程中应

         该确保应用程序的内存足够可用,CMS不会等到应用程序饱和的时候在去回收垃圾,而是到达某个阈值的

         时候就去回收,回收的阈值可以通过指定的参数来设置-XX:CMSInitiatingOccupancyFraction来指定,默

         认值是68,也就是说当老年代的使用率达到68%的时候会执行CMS垃圾回收,如果内存使用增长的很快,

         在CMS过程中出现了内存不足的情况,CMS回收就会失败,虚拟机将启用老年代串行回收器进行垃圾回收,

         这会导致应用程序中断,直到垃圾回收完成后才会正常工作,这个过程GC停顿时间可能过长,所以设置

         -XX:CMSInitiatingOccupancyFraction要根据实际情况。

         如下两个参数:

        -XX:UseCMSCompactAtFullCollection:使CMS完成之后进行碎片整理。

        -XX:CMSFULLGCsBeforeCompaction:设置多少次CMS回收后对内存进行一次压缩。

(4)G1回收器:是JDK1.7中提出的垃圾回收器,从长期目标看是为了取代CMS回收器,属于分代垃圾回收器

         区分新生代和老年代,依然有eden和from、to区,它不要求新生代、老年代、eden、from、to区空间

         都连续,使用了分区算法。据说JDK1.7之后使用的是G1回收算法,不过有待考证,目前不能确定,但是

         在JDK1.7里它还不太成熟。

         并行性:G1回收期间可多线程同时工作。

         并发性:G1拥有与应用程序交替执行的能力,部分工作与应用程序同时进行,在整个GC期间不会完全阻

         塞应用程序。

         分代GC:G1依然是一个分代的收集器,但是它兼顾新生代和老年代一起工作,之前的垃圾回收器或者在

         新生代工作或者在老年代工作,因此这是一个很大的不同。

         空间整理:G1在回收过程中不会像CMS那样在经过若干次GC后需要进行碎片整理,G1采用了有效复制对

         象的方式减少空间碎片。

         可预见性:由于分区的原因,G1可以只选取部分区域进行回收,缩小了回收范围,提升了性能   

         使用:-XX:+UseG1GC应用G1收集器

         使用:-XX:MaxGCPauseMillis指定最大停顿时间

         使用:-XX:ParallelGCThreads设置并行回收的线程数量。

                  

 

 

 

 

 

猜你喜欢

转载自blog.csdn.net/jialanshun/article/details/79211522