zoukankan      html  css  js  c++  java
  • JVM虚拟机------运行时数据区------堆

    知识点

    1. 多线程共享一个堆内存,且是最大的内存空间
    2. Java内存管理的核心区域
    3. 在JVM启动的时候创建、大小确定(但是可以调节)
    4. 《Java虚拟机规范》规定:堆可以处于物理上不连续的内存空间中,当在逻辑上它应该被视为连续的
    5. 其实在堆中,存在线程私有的缓冲区------》Thread Local Allocation Buffer, TLAB.
    6. “几乎”所有的对象实例都在这里分配内存。
    7. 栈帧中只会保留引用,这个引用指向对象或者数组的堆中的位置
    8. 方法结束后,堆中的对象不会马上被移除,仅仅在垃圾收集的时候才会被移除
    9. 堆,是GC执行垃圾回收的重点区域

    步骤:

    1. .class文件通过类加载到方法区中
    2. 栈根据方法区的类,实例化对象,对象的信息存储在堆当中

    堆内存----细分

    JDK7

    新生区:Edn区、Survivor1、Survivor2

    养老区:

    永久区:--------在方法区当中

    JDK8

    新生区:Edn区、Survivor1、Survivor2

    养老区:

    元空间:--------在方法区当中

     设置堆空间大小与OOM

    Java堆区用于存储Java对象实例,在JVM启动时就已经设定好了

    “-Xms”:用户表示堆区的起始内存,等价于 -XX:initialHeapSize

    “-Xmx”:则用于表示堆区的最大内存,等价于-XX:MaxHeapSize

    一旦堆区中的内存大小超过-Xmx所指定的最大内存,就会抛出OOM异常

    通常会将 -Xms 和-Xmx两个参数配置相同的值,其目的是为了能够在java垃圾回收机制清理完堆区后不需要重新分隔计算堆区的大小,从而提高性能。

    默认情况:初始内存大小:物理电脑内存大小 / 64

         最大内存大小:物理内存大小 / 4

    假如没有设置初始化堆大小,但是设置了最大堆大小,堆的大小会自动调整

    public class HeapSpaceInitial {
    
        public static void main(String[] args) {
    
            //返回Java虚拟机中的堆内存总量,(单位字节)
            long initialMemory = Runtime.getRuntime().totalMemory() / 1024 / 1024;
            //返回Java虚拟机试图使用的最大堆内存量
            long maxMemory = Runtime.getRuntime().maxMemory() / 1024 / 1024;
    
            System.out.println(initialMemory); M
            System.out.println(maxMemory);  M
    
            System.out.println(initialMemory * 64 / 1024); //15G
            System.out.println(maxMemory * 4 / 1024);  //14G
        }
    }

    手动设置初始化堆大小与最大堆内存

     皮一下吧

    假如设置的初始化堆大小 < 设置的最大堆内存

     起飞,果然不行!!!

    查看堆内存大小参数

    方式1:jps    /  jstat -gc 进程id

    方式2:-XX:+PrintGCDetails 

     (25600 + 4096 * 2 + 68608)  / 1024 = 100M

    年轻代与老年代

    存储在JVM中的Java对象可以划分为两类:

    活得短的与活的久的

    分为:年轻代 + 老年代

    年轻代结构

     老年代结构

     调整   《新生代和老年代》   在堆结构的占比

     默认堆分为3份,新生代占1份,老年代占2份

    通过上述命令:

            新生代占1份,老年代占4份

    调整《年轻代,Eden空间和另外两个Survivor空间比例》

    默认年轻代配比:Eden占8份,Survivor0占1份,Survivor1占1份

    通过上述指令:

            Eden占10份,Survivor0占1份,Survivor1占1份

    设置《年轻代大小》

     百分之80的对象都在Eden区死掉了

    其它指令

    -XX:-UseAdaptiveSizePolicy

    对象分配过程

    1. new的对象先放伊甸园区,此区有大小限制
    2. 当伊甸园的空间填满时,程序有需要创建对象,JVM的垃圾回收器将对伊甸园区进行垃圾回收(Minor GC),将不再被引用的对象进行销毁,再加载新的对象放在伊甸园区
    3. 然后对伊甸园区中的剩余对象移动到幸存者0区
    4. 如果再次触发垃圾回收,此时上次幸存下来的放到幸存者0区的,如果没有回收,就会放到幸存者1区
    5. 如果再次经历垃圾回收,此时会重新放回幸存者0区,接着再去幸存者1区
    6. 啥时候去养老区?可以设置次数,默认15次
      1. -XX:MaxTenuringThreshold = <N>进行设置

    第一次清理:

     第二次清理:也就是将S0的所有转移到S1,并将Eden内存活下来的类转移到S1,当中,此时S0为空了

     第三次清理:假如S1满了,那么就将部分转移到永久区 + S0区,此时S1区就空了

     总结:

    针对幸存者S0,S1,复制之后有交换,谁空谁是to

    关于垃圾回收:频繁在新生去收集,很少在养老区收集,几乎不再永久区/元空间收集

     常用调优工具

     Minor GC、Major GC、Full GC

    JVM在进行GC时,并非每次都对上面三个内存(新生代、老年代、方法区)区域一起回收的,大部分时候回收的都是新生代

    针对HotSpot VM的实现,它里面的GC按照回收区域又分为两大类型:一种是部分收集(Partial GC),一种是整堆收集(Full GC)

    Partial GC(部分收集)

    不是完整收集整个Java堆的垃圾收集。其中又分为:

      新生代收集(Minor GC/Young GC):只是新生代(Eden、S0、S1)的垃圾收集

      老年代收集(Major GC/old GC):只是老年代的垃圾收集

        目前,只有CMS GC会有单独收集老年代的行为。

        注意:很多时候Major GC会和Full GC混淆使用,需要具体分辨是老年代回收还是整堆回收。

      混合收集(Mixed GC):收集整个新生代以及部分老年代的垃圾收集。

        目前,只有G1 GC会有这种行为

    整堆收集(Full GC):

    收集整个Java堆和方法区的垃圾收集

    年轻代GC(Minor GC)触发机制:

    当年轻代空间不足时,就会触发Minor GC,这里的年轻代指的是Eden满,Survivor满是不会引发GC;每次Minor GC会清理年轻代内存

    Minor GC非常频繁,一般回收速度比较快

    Minor GC会引发STW,暂停其它用户的线程,等垃圾回收结束,用户线程才恢复运行

    老年代GC(MajorGC/Full GC)触发机制:

    指发生在老年代的GC,对象从老年代消失时,我们说Major GC或者Full GC发生了

    出现Major GC,经常会伴随至少一次的Minor GC,但是不是绝对的-----一般的,老年代空间不足时,会先尝试Minor GC,如果之后空间还不足,则触发major GC

    Major GC的速度一般会比Minor GC慢10倍,STW的时间更长

    Major GC后,内存还不足,就报OOM

    Full GC触发机制

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

    老年代空间不足

    方法区空间不足

    通过Minor GC后进入老年代的平均大小    >   老年代的可用内存

    由Eden区、survivor space0(from space)区向survivor space1(To space)区复制时,对象大小大于To space可用内存,则把该对象转存到老年代,且老年代的可用内存 < 该对象的大小

    full GC是开发或调优中尽量避免的,这样暂时时间会短一些

    堆空间分代思想

     

    为什么需要Java堆分代?不分代就不能正常工作吗?

    答:百分之80的对象都会死的很快,优化GC,免得每次上来就是整体扫描

    内存分配策略(或者对象Promotion规则)

    对象出生在Eden中-----经过一次Minor GC后存活-----》被Survivor容纳,该对象年龄设置为1-------》往后该对象在Survivor区中每活过一次Minor GC,年龄+1--------》当年龄 》 15,即经过了15次Minor GC,那么就晋升老年代

    这个15阈值,可以通过-XX:MaxTenuringThreshold设置

    一些原则

    针对不同年龄段的对象分配原则如下:

      优先分配到Eden

      大对象直接分配到老年代

        尽量避免大对象

      长期存活的对象分配到老年代

      动态对象年龄判断

        如果Survivor区中的相同年龄对象的大小总和大于Survicor空间的一半,年龄大于或等于该年龄的对象直接进入老年代,无须等到maxTenuringThreshold

      空间分配担保

        -XX:HandlePromotionFailure

  • 相关阅读:
    【转】iOS深入学习(Block全面分析)
    iOS—请求Web Service
    iOS设计模式——MVC
    iOS基础知识
    iOS学习——常用博客
    【转】使用segue页面间传递数据
    【转】storyboard之 prepareForSegue:sender:
    【转】NSDictionary和NSMutableDictionary用法详解
    配置.pch文件
    MKNetworkKit下载图片并显示在UIImageView上
  • 原文地址:https://www.cnblogs.com/sicheng-li/p/12977109.html
Copyright © 2011-2022 走看看