• 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(十六进制的线程号)   查看日志

  • 相关阅读:
    JMeter--聚合报告之 90% Line 正确理解
    jmeter--函数助手对话框之参数详解
    测试理论--如何根据需求设计测试用例
    java jdk 1.6 下载
    linux磁盘满时,如何定位并删除文件
    linux mysql 新增用户 分配权限
    Hibernate 中多对多(many-to-many)关系的查询语句
    springMVC的url-pattern /和/*的区别
    thinkphp多表关联并且分页
    thinkphp 模板里嵌入 php代码
  • 原文地址:https://www.cnblogs.com/ttaall/p/12870975.html
Copyright © 2020-2023  润新知