JVM基础知识

       JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。

JVM内存组成结构

wKiom1Xs9aWhN3OEAABJZEwuJ-c836.gif

1、本地方法栈

用于支持native方法的执行,存储了每个native方法调用的状态

2、方法区

存放了要加载的类信息、静态变量、final类型的常量、属性和方法信息。JVM用持久代(Permanet Generation)来存放方法区,可通过-XX:PermSize和-XX:MaxPermSize来指定最小值和最大值

3、JAVA栈

       在jvm中栈用来存储一些对象的引用(例如方法引用)、基本数据类型(boolean、byte、char、short、int、float、long、double)。它的存储比堆快得多,只比CPU里的寄存器慢。每个线程都有自己的Stack Space(栈),在JDK5.0之前每个线程栈大小为256k,JDK5.0之后改为1M。

*使用递归时如果深度过深很容易导致栈溢出(StackOverflowError),此时可以通过设置栈大小来处理(-Xss:如-Xss128k)

测试用例:

public class StackTest{

    private static int count=0;

    public static void add(long a,long b){
        count++;
        add(a,b);
    }
    public static void main(String args[]){
        try{
            add(0L,0L);
        }catch(Throwable e){
            System.out.println("深度为: "+count);
            System.exit(0);
            e.printStackTrace();
        }
    }
}

-Xss分别设置成128k和256k结果分别为:

深度为: 8964(128k)

深度为: 17700 (256K)

4、JAVA堆

fae5c8bb40e8dfb8b7968d933aafd71bc77.jpg

一:新生代:主要是用来存放新生的对象。一般占据堆的1/3空间。由于频繁创建对象,所以新生代会频繁触发MinorGC进行垃圾回收。

         新生代又分为 Eden区、ServivorFrom、ServivorTo三个区。

         Eden区:Java新对象的出生地(如果新创建的对象占用内存很大,则直接分配到老年代)。当Eden区内存不够的时候就会触发MinorGC,对新生代区进行一次垃圾回收。

         ServivorTo:保留了一次MinorGC过程中的幸存者。

         ServivorFrom:上一次GC的幸存者,作为这一次GC的被扫描者。

         MinorGC的过程:MinorGC采用复制算法。首先,把Eden和ServivorFrom区域中存活的对象复制到ServicorTo区域(如果有对象的年龄以及达到了老年的标准,则赋值到老年代区),同时把这些对象的年龄+1(如果ServicorTo不够位置了就放到老年区);然后,清空Eden和ServicorFrom中的对象;最后,ServicorTo和ServicorFrom互换,原ServicorTo成为下一次GC时的ServicorFrom区。

 

二:老年代:主要存放应用程序中生命周期长的内存对象。

    老年代的对象比较稳定,所以MajorGC不会频繁执行。在进行MajorGC前一般都先进行了一次MinorGC,使得有新生代的对象晋身入老年代,导致空间不够用时才触发。当无法找到足够大的连续空间分配给新创建的较大对象时也会提前触发一次MajorGC进行垃圾回收腾出空间。

    MajorGC采用标记—清除算法:首先扫描一次所有老年代,标记出存活的对象,然后回收没有标记的对象。MajorGC的耗时比较长,因为要扫描再回收。MajorGC会产生内存碎片,为了减少内存损耗,我们一般需要进行合并或者标记出来方便下次直接分配。

     当老年代也满了装不下的时候,就会抛出OOM(Out of Memory)异常。

 

 三:永久代

    指内存的永久保存区域,主要存放Class和Meta(元数据)的信息,Class在被加载的时候被放入永久区域. 它和和存放实例的区域不同,GC不会在主程序运行期对永久区域进行清理。所以这也导致了永久代的区域会随着加载的Class的增多而胀满,最终抛出OOM异常。

    在Java8中,永久代已经被移除,被一个称为“元数据区”(元空间)的区域所取代。

    元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制。类的元数据放入 native memory, 字符串池和类的静态变量放入java堆中. 这样可以加载多少类的元数据就不再由MaxPermSize控制, 而由系统的实际可用空间来控制.

    采用元空间而不用永久代的几点原因:(参考:http://www.cnblogs.com/paddix/p/5309550.html)

  1、为了解决永久代的OOM问题,元数据和class对象存在永久代中,容易出现性能问题和内存溢出。

  2、类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出(因为堆空间有限,此消彼长)。

  3、永久代会为 GC 带来不必要的复杂度,并且回收效率偏低。

*堆内存设置

-Xmx:最大堆内存,如:-Xmx512m  (默认是物理内存的1/4)

-Xms:初始时堆内存,如:-Xms256m  (默认是物理内存的1/64)

-XX:MaxNewSize:最大年轻区内存

-XX:NewSize:初始时年轻区内存.通常为 Xmx 的 1/3 或 1/4。新生代 = Eden + 2 个 Survivor 空间。实际可用空间为 = Eden + 1 个 Survivor,即 90%

-XX:MaxPermSize:最大持久带内存

-XX:PermSize:初始时持久带内存

-XX:+PrintGCDetails。打印 GC 信息

 -XX:NewRatio 新生代与老年代的比例,如 –XX:NewRatio=2,则新生代占整个堆空间的1/3,老年代占2/3

 -XX:SurvivorRatio 新生代中 Eden 与 Survivor 的比值。默认值为 8。即 Eden 占新生代空间的 8/10,另外两个 Survivor 各占 1/10

 

JVM GC触发

1.Minor GC

从年轻代空间(包括 Eden 和 Survivor 区域)回收内存被称为 Minor GC(比如当 Eden 区满了)

2.Major GC 

是清理老年代(一般老年代空间不足时触发)。

3.Full GC 

是清理整个堆空间—包括年轻代和老年代

Full GC触发条件:

(1)调用System.gc时,系统建议执行Full GC,但是不必然执行

(2)老年代空间不足

(3)方法区空间不足

(4)通过Minor GC后进入老年代的平均大小大于老年代的可用内存

(5)由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小
 

 

转载于:https://my.oschina.net/u/731542/blog/3010976

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值