JVM学习笔记(一)

一、jvm内存模型总体分类

     1, 程序计数器(program count register ):

           多线程时,当线程数超过CPU数量或CPU内核数量,线程之间就要根据时间片抢夺CPU时间资源。因此每个线程要有一个独立的程序计数器,记录下一条要运行的指令。

           线程私有的内存区域。

           如果执行的是JAVA方法,计数器记录正在执行的java字节码地址;如果执行的是native方法,则计数器为空。

     2,虚拟机栈(VM Stack)

          线程私有的,与线程在同一时间创建。

         管理java方法执行的内存模型。

         每个方法执行时都会创建一个帧栈来存储方法的变量表、操作数栈,动态链接方法,返回值,返回地址等信息。

        如果请求的栈深度大于最大可大深度,则抛出StackOverflowError;如果栈是可动态扩展的,但没有内存空间支持扩展,则抛出OutofMemoryError。

    3,本地方法区(native method area )

       线程私有的

        和虚拟机功能相似,但管理的不是java方法,是本地方法,本地方法是用C实现的。

    4,java堆(java heap)

        线程共享的,存放所有对象的实例和数组。

        垃圾回收的主要区域。可以分为新生代和老生代(tenured)

        新生代用于存放刚创建的对象以及年轻的对象,如果对象一直没有被回收,生存得足够长,老年对象就会被移入老年代。

        新生代又可进一步细分为eden、survivorSpace0(s0,from space)、survivorSpace1(s1,to space)。

        刚创建的对象都放入eden,s0和s1都至少经过一次GC并幸存。如果幸存对象经过一定时间仍存在,则进入老年代(tenured)。  

    5,方法区(method area )

   线程共享的,用于存放被虚拟机加载的类的元数据信息:如常量、静态变量、即时编译器编译后的代码。

   称为永久代。

   如果hotspot虚拟机确定一个类的定义信息不会被使用,也会将其回收。

   回收的基本条件至少有:所有该类的实例被回收,而且装载该类的ClassLoader被回收

二、垃圾回收算法

    1标记-清除算法(Mark-Sweep)

从根节点开始标记所有可达对象,其余没标记的即为垃圾对象,执行清除。但回收后的空间是不连续的。

    2复制算法(copying)

将内存分成两块,每次只使用其中一块,垃圾回收时,将标记的对象拷贝到另外一块中,然后完全清除原来使用的那块内存。复制后的空间是连续的。

复制算法适用于新生代,因为垃圾对象多于存活对象,复制算法更高效。在新生代串行垃圾回收算法中,将eden中标记存活的对象拷贝未使用的s1中,s0中的年轻对象也进入s1,如果s1空间已满,则进入老年代;这样交替使用s0s1。这种改进的复制算法,既保证了空间的连续性,有避免了大量的内存空间浪费。,

    3标记-压缩算法(Mark-compact)

适合用于老年代的算法(存活对象多于垃圾对象)。

标记后不复制,而是将存活对象压缩到内存的一端,然后清理边界外的所有对象。

三,JVM区域总体分两类,heap区和非heap:

1heap区又分:Eden Space(伊甸园)、Survivor Space(幸存者区)Tenured Gen(老年代-养老区)。

  非heap区又分:Code Cache(代码缓存区)Perm Gen(永久代)、Jvm Stack(Java虚拟机栈)Local Method Statck(本地方法栈)

2HotSpot虚拟机GC算法采用分代收集算法:

 (1)、一个人(对象)出来(new 出来)后会在Eden Space(伊甸园)无忧无虑的生活,直到GC到来打破了他们平静的生活。GC会逐一问清楚每个对象的情况,有没有钱(此对象的引用)啊,因为GC想赚钱呀,有钱的才可以敲诈嘛。然后富人就会进入Survivor Space(幸存者区),穷人的就直接kill掉。

 (2)、并不是进入Survivor Space(幸存者区)后就保证人身是安全的,但至少可以活段时间。GC会定期(可以自定义)会对这些人进行敲诈,亿万富翁每次都给钱,GC很满意,就让其进入了Genured Gen(养老区)。万元户经不住几次敲诈就没钱了,GC看没有啥价值啦,就直接kill掉了。

 (3)、进入到养老区的人基本就可以保证人身安全啦,但是亿万富豪有的也会挥霍成穷光蛋,只要钱没了,GC还是kill掉。

3分区的目的:

  新生区由于对象产生的比较多并且大都是朝生夕灭的,所以直接采用标记-清理算法。而养老区生命力很强,则采用复制算法,针对不同情况使用不同算法。,

4heap区域中Perm Gen永久代中放着类、方法的定义,jvm Stack虚拟机栈区域放着方法参数、局域变量等的引用,方法执行顺序按照栈的先入后出方式。


5下面对每一个内存区域做详细介绍。

     Eden Space字面意思是伊甸园,对象被创建的时候首先放到这个区域,进行垃圾回收后,不能被回收的对象被放入到空的survivor区域。

     Survivor Space幸存者区,用于保存在eden space内存区域中经过垃圾回收后没有被回收的对象。Survivor有两个,分别为To SurvivorFrom Survivor,这个两个区域的空间大小是一样的。执行垃圾回收的时候Eden区域不能被回收的对象被放入到空的survivor(也就是To Survivor,同时Eden区域的内存会在垃圾回收的过程中全部释放),另一个survivor(即From Survivor)里不能被回收的对象也会被放入这个survivor(即To Survivor),然后To Survivor Eden SpaceSurvivor Space都属于新生代

  新生代中执行的垃圾回收被称之为Minor GC(因为是对新生代进行垃圾回收,所以又被称为Young GC,每一次Young GC后留下来的对象age1

   注:GCGarbage Collection,垃圾回收。

      Old Gen老年代,用于存放新生代中经过多次垃圾回收仍然存活的对象,也有可能是新生代分配不了内存的大对象会直接进入老年代。经过多次垃圾回收都没有被回收的对象,这些对象的年代已经足够old了,就会放入到老年代。

  当老年代被放满的之后,虚拟机会进行垃圾回收,称之为Major GC由于Major GC除并发GC外均需对整个堆进行扫描和回收,因此又称为Full GC

      heap区即堆内存,整个堆大小=年轻代大小 + 老年代大小。堆内存默认为物理内存的1/64(<1GB);默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制,可以通过MinHeapFreeRatio参数进行调整;默认空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制,可以通过MaxHeapFreeRatio参数进行调整。

 

6下面我们来认识下非堆内存(非heap区)

    Code Cache代码缓存区,它主要用于存放JIT所编译的代码。CodeCache代码缓冲区的大小在client模式下默认最大是32m,在server模式下默认是48m,这个值也是可以设置的它所对应的JVM参数为ReservedCodeCacheSize InitialCodeCacheSize,可以通过如下的方式来为Java程序设置。

      -XX:ReservedCodeCacheSize=128m

     CodeCache缓存区是可能被充满的,当CodeCache满时,后台会收到CodeCache is full的警告信息,如下所示:

    “CompilerThread0”java.lang.OutOfMemoryError: requested 2854248 bytes for Chunk::new. Out of swap space?

注:JIT编译器是在程序运行期间,将Java字节码编译成平台相关的二进制代码。正因为此编译行为发生在程序运行期间,所以该编译器被称为Just-In-Time编译器。

      Perm Gen全称是Permanent Generation space,是指内存的永久保存区域,因而称之为永久代。这个内存区域用于存放ClassMeta的信息,Class在被 Load的时候被放入这个区域。因为Perm里存储的东西永远不会被JVM垃圾回收的,所以如果你的应用程序LOAD很多CLASS的话,就很可能出现PermGen space错误。默认大小为物理内存的1/64

 

四,JVM参数:

Java -Xms2g -Xmx2g -Xmn512M -Xss128K -XX:PermSize=128M -XX:MaxPermSize=128M -XX:NewRatio=4 -XX:SurivorRatio=4 -XX:MaxTenuringThreshold=1

-Xms2gJVM启动初始化堆大小为2gXms的默认是物理内存的1/64但小于1G

-Xmx2gJVM最大的堆大小为2gXmx默认是物理内存的1/4但小于1G;将-Xms-Xmx的值配置为一样,可以避免每次垃圾回收完成后对JVM堆大小进行重新的调整。

-Xmn512M:堆中的新生代大小为512M

-Xss128K:每个线程的堆栈大小为128K

-XX:PermSize=128MJVM持久代的初始化大小为128M

-XX:MaxPermSize=128MJVM持久代的最大大小为128M

-XX:NewRatio=4JVM堆的新生代和老年代的大小比例为14

-XX:SurvivorRatio=4:新生代Surivor区(新生代有2Surivor区)和Eden区的比例为24

      EdenSurvivor的占用比例。例如8表示,一个survivor区占用 1/8 Eden内存,即1/10的新生代内存,为什么不是1/9因为我们的新生代有2survivor,即S1S22所以survivor总共是占用新生代内存的 2/10Eden与新生代的占比则为 8/10

-XX:MaxHeapFreeRatio  GC后,如果发现空闲堆内存占到整个预估的比例小于这个值,则减小堆空间。

-XX:MaxTenuringThreshold=1:新生代的对象经过几次垃圾回收后(如果还存活),进入老年代。如果该参数设置为0,这表示新生代的对象在垃圾回收后,不进入survivor区,直接进入老年代

 

Java -XX:+UseParallelGC -XX:ParallelGCThread=4 -XX:+UseParallelOldGC -XX:MaxGCPauseMillis=100 -XX:+UseAdaptiveSizePolicy

-XX:+UseParallelGC:使用并行的垃圾收集器,但仅针对新生代有效,老年代仍然使用串行收集器

-XX:ParallelGCThread=4:设置并行垃圾回收器的线程为4个,该设置最好与处理器的数目相同

-XX:+UseParalleOldGC:配置老年代使用并行垃圾收集器,JDK1.6支持老年代使用并行收集器

-XX:MaxGCPauseMillis=100:设置每次新生代每次收集器垃圾回收的最长时间,如果无法满足该时间,JVM会自动调整新生代区的大小,以满足该值

-XX:+UseAdaptiveSizePolicy:设置此值后,JVM会自动调整新生代大小以及相应的surivor区的比例,以达到设置的最低响应时间或者收集频率等

 

Java -XX:UseConcMarkSweepGC -XX:+UseParNewGC -XX:CMSFullGCsBeforeCompaction=5 -XX:+UseCMSCompactAtFullCollection

-XX:UseConcMarkSweepGC:设置JVM堆的老年代使用CMS并发收集器,设置该参数后,-XX:NewRatio参数失效,但-Xmn参数依然有效

-XX:UseParNewGC:设置新生代使用并发收集器,在JDK1.5以上,JVM会根据系统自动设置

-XX:CMSFullGCsBeforeCompaction=5:设置5CMSGC后对堆空间进行压缩、整理

-XX:+UseCMSCompactAtFullCollection:打开对老年代的压缩,可能会影响性能,但可以消除堆碎片

提示:Heap Size 最大不要超过可用物理内存的80%,一般的要将-Xms和-Xmx选项设置为相同,而-Xmn为1/4的-Xmx值。

你可能感兴趣的:(java基础知识回顾,java面试)