zoukankan      html  css  js  c++  java
  • JVM底层原理 内存模型+GC垃圾回收

    java常用指令:   

    javac -> 编译    java -> 运行

    jps -> 查看当前java相关进程  

    jinfo ->查看某一参数具体值

    jinfo -flags 进程号    打出该java进程下所有JVM配置信息

    jinfo  -flag  PrintGCDetails  进程号 ->  查看某个java进程 是否开启某个参数(PrintGCDetails打印GC详情)                

    jinfo  -flag  MetaSpace  进程号->  查看某个Java进程 元空间大小

    java -XX:+PrintCommandLineFlags  查看GC垃圾回收器

    JVM堆内存空间分配:

     

    方法区是一种概念,规范,元空间(永久代)是方法区的实现,包括:装载后类的信息,静态变量,常量池,即时编译后的代码

    元空间/永久代内 什么情况下会发生垃圾回收?

    1. 该类的所有对象都已经在堆内存中被回收

    2.该类的类加载器已经被回收

    3.该类的class对象都没有任何引用

    满足三个条件则该类会在方法区内被回收

    基于Hotspot虚拟机的TLAB

    一般常说,栈是线程私有的,堆是共享的。 这句话并不完全正确,堆中Eden区会为每个线程预先留一小块内存空间用来创建对象 (称之为TLAB分配,即Thread Local Allocation Buffer)

    1. 为什么这么做? 

    防止多线程同时要去堆内存开辟空间,可能会造成多个线程争抢同一块内存地址并发问题

    2. TLAB特点?

    每个线程在Eden区开辟的一小块空间,

    只是在“分配”这个动作上是线程独享的,至于在读取、垃圾回收等动作上都是线程共享的,

    TLAB空间的内存也非常小,默认情况下仅占有整个Eden空间的1%。

    3. TLAB带来的问题?

    如果一个对象需要的空间大小超过TLAB中剩余的空间大小,则直接在堆内存中对该对象进行内存分配。

    如果一个对象需要的空间大小超过TLAB中剩余的空间大小,则废弃当前TLAB,重新申请TLAB空间再次进行内存分配。

    总结来说就是TLAB的剩余大小不满足新创建的对象大小,虚拟机定义了一个refill_waste的值,“最大浪费空间”。新创建的对象大于这个值则直接在堆内存分配,否则废弃当前TLAB 重新申请再分配。

    System.gc()的理解

    1.在默认情况下,通过System. gc()或者Runtime . getRuntime() .gc()的调用,会显式触发Full GC,同时对老年代和新生代进行回收,尝试释放被丢弃对象占用的内存。(System. gc()的底层就是Runtime . getRuntime() .gc() )
    2.System. gc()只是提醒JVM的垃圾回收器执行Full GC,但是不确定是否马上执行,还有可能完全被拒绝。System. gc()与Runtime . getRuntime().gc()的作用一样。

    四大垃圾回收算法

    1.引用计数法

    2.复制算法    缺点:浪费空间   优点:不会产生内存碎片

    作用于年轻代Eden区 From区和To区     复制-》清空-》交换  (复制之后有交换,谁空谁为To区)Eden区满了触发young区垃圾回收,对Eden和From区进行一次垃圾回收,将两个区剩余的对象复制一份到To区,然后清空,然后To和From交换。如果对象达到老年标准则进入老年代(交换15次)。

    3.标记清除算法    先标记再清除  优点:节约内存空间    缺点:产生内存碎片

    4.标记整理算法     先标记,再清除,最后整理把多余的碎片整理成整团      在标记清除算法上,不会产生内存碎片,增加了整理的耗时   

    七大垃圾回收器

    串行  并行  并发  G1  ——四种主要垃圾回收器

    串行垃圾回收器: 为单线程设计,只使用一个线程进行垃圾回收,执行垃圾回收同时会暂停用户线程

    并行垃圾回收器:串行垃圾回收器的加强版,有多个线程进行垃圾回收,执行垃圾回收的同时会暂停用户线程

    并发垃圾回收器: 垃圾回收器线程和用户线程交叉执行,能做到尽量小的停顿用户线程(会产生内存碎片)

    G1垃圾回收器: 将堆内存分割成不同区域,并发进行垃圾回收

    并发标记清除GC——CMS: 用于老年代  (希望最小时间停顿收集的GC回收器   一般用于大型互联网应用垃圾回收)  

     1 初始标记(标记一下GC Roots能直接关联的对象,也就是静态变量和栈引用的局部变量)      2 并发标记(标记所有GC Root的间接引用对象)      3 再次标记(再标记第二阶段里新创建的对象,是否GC 引用到)      4 并发清除 

     只在首次标记和再次标记时是完全停顿的,并发标记和并发清除的时间都可与应用线程并发进行。

    最耗时阶段在于 并发标记与并发清除阶段,好在两阶段都可以多线程执行 并且不需要STW。 

    优点:保持较小停顿          缺点: 1 对CPU压力很高   2 产生内存碎片(可以设置一个参数 多少次full GC之后进行一次压缩full GC整理碎片)

    老年区串行收集器作为CMS收集失败的后备收集器.

    该参数配置为 CMS垃圾回收器多少次Full GC后会执行一次标记整理算法

    G1垃圾回收器——G1:目的为了取代jdk8之前的CMS垃圾收集器

    1.物理上不区分年轻代老年代,将整个堆内存分成规格大小的豆腐块Region(1-32m大小)  ,每个豆腐块属性标识了新生代,Eden,Survivor,养老代等标记,物理上不是连续的,逻辑上连续

    Humongous-大对象存储  ,

    2.整体采用标记整理算法,局部采用复制算法,不会产生内存碎片

    2.GC能与应用程序并发执行,高吞吐量同时尽量减少停顿时间

    3.预测GC停顿时间机制,用户可以指定期望停顿时间,GC垃圾收集会尽量小于期望时间(根据停顿时间去收集垃圾最多的豆腐块区域)

    优点:没碎片,减少停顿,期望精准停控时间

    G1回收过程

    小区域内收集+连续内存块

     

    GCRoots

    如何判断一个对象是否可被回收:   枚举根节点做可达性分析(根搜索路径)

                      

     从GC根开始通过引用关系进行遍历,遍历不到的节点对象视为死亡。

    GCRoots -> GC根的对象:1.栈中局部变量引用的对象   2.方法区中类静态属性引用的对象   3.方法区中常量引用的对象   4.本地方法中Native引用的对象

    finalize关键字,

    用作GCRoot时垂死挣扎的一次机会

    发生GC时,会先判断对象是否执行过finalize 方法,如果未执行,则会先执行 finalize 方法,我们可以在此方法里将当前对象与 GC Roots 关联,这样执行 finalize 方法之后,GC 会再次判断对象是否可达,

    如果不可达,则会被回收,如果可达,则不回收!

    finalize 方法只会被执行一次,如果第一次执行 finalize 方法此对象变成了可达确实不会回收,但如果对象再次被 GC,则会忽略 finalize 方法,对象会被回收!这一点切记!

    常见JVM参数:

    java -XX:+Print Flags Initial    查看JVM默认初始参数值

    java  -XX:+Print Flags final    查看JVM修改后的参数值   =    :=

    JVM参数:

    1.标配参数   自打jdk娘胎出来 一直很稳定几乎没变化 如  java -version  java -help

    2.-X参数    默认 -Xmixed混合模式

    3.-XX参数   

      3.1. Boolean类型参数  · -XX: -PrintGCDetails  关闭打印GC收集细节             -XX:+PrintGCDetails 打开打印GC收集细节

      3.2. K-V设值类型参数   -XX: -MetaspaceSize=128m    

      

     -Xms  初始化堆内存大小   等价于 -XX:initialHeapSize    默认大小物理内存的1/64

      -Xmx 最大化堆内存大小    等价于  -XX:MaxHeapSize     默认物理内存1/4

     -Xss  单个线程初始栈空间             等价于ThreadStackSize   默认512k-1024k

    其他参数包括: 设置伊甸区,from区和to区比例(默认8:1:1)   设置新生区和养老区比例 (默认1:2)  设置新生区经过多少轮GC进入老年区(默认15次)

    引用问题Reference: 

    Reference 强引用,  永远不会被垃圾GC回收的对象,即使死都不回收

    SoftReference 软引用,  内存足够时不会回收,内存不足时会GC回收          应用场景:大量加载图片 每次从本地读取大量图片消耗性能,全部缓存到内存,可能内存不足,由此提出软引用和弱引用。

    WeekReference 弱引用,  只要GC就会被回收

    PhantomReference 虚引用   用于监控一个对象的回收状态  必须与引用队列联合使用  

    ReferenceQueue queue = new ReferenceQueue();

    Object obj1 = new Object();

    PhantomReference phantom = new PhantomRefence(obj,queue);

    WeekHashMap  key为 弱引用,如果Key被置空,底层Node<key,value>节点就会被回收。

    PhantomReference维护一个队列,

    PhantomReference的引用级别属于弱引用,并且调用获取对象的方法永远返回null

    作用是: 如果PhantomReference引用的对象被回收,PhantomReference会加入到队列中,通过queue.poll()获取,因此可判断出他的对象是不是被回收了,

    判断之后我们就可以相应的处理,可参考DirectByteBuffer的Cleaner

    OOM篇

    StackOverFlowError

    OutOfMemoryError:  java heap space   栈溢出

    OutOfMemoryError: GC overhead limit exceeded     GC回收时间过长,用了98%的时间,回收不到2%的内存。由于每次只回收2% 导致内存又快速被占满 导致再次GC

    OutOfMemoryError: Direct buffer memory   Native本地内存满了。  写NIO程序时常用ByteBuffer来读写数据,一种基于通道Channel,缓冲区Buffer的I/O方式,可以通过Native函数库直接在堆外分配内存给读写数据,然后堆内对象DirectByteBuffer引用这块Native内存。

    OutOfMemoryError: unable to create new native Thread     创建了过多的线程,一个进程创建这么多的线程超出了系统承载

    OutOfMemoryError:  MetaSpace

    排查实战;

    top 命令 找到cpu占比最高的进程

    ps -ef 找到对应的进程号

    ps -mp 进程号 -o  thread,tid,time    找出进程中具体的线程号 tid

    将10进制的线程号转为16进制 (英文字母小写)

    jstack 进程号 | grep tid(十六进制的线程号)   查看日志

  • 相关阅读:
    监控里的主码流和子码流是什么意思
    监控硬盘容量计算
    一个能让你了解所有函数调用顺序的Android库
    电工选线
    oracle linux dtrace
    list all of the Oracle 12c hidden undocumented parameters
    Oracle Extended Tracing
    window 驱动开发
    win7 x64 dtrace
    How to Use Dtrace Tracing Ruby Executing
  • 原文地址:https://www.cnblogs.com/ttaall/p/12870975.html
Copyright © 2011-2022 走看看