JVM&JMM

53 篇文章 0 订阅
53 篇文章 0 订阅

JVM

JVM是什么?

线程共享部分:
  1. 方法区:包括Class元数据、常量、静态变量、运行时常量池、JIT编译后的机器码也在方法区。
  2. 堆区:实例对象、字符串常量池。
线程独享部分:
  1. 程序计数器(PC寄存器):指向当前正在运行的指令地址。
  2. Java虚拟机栈(栈内存):保存局部变量表、基本数据类型、堆内存中对象引用变量以及方法返回地址。
  3. 本地方法栈:调度native方法。
  • 什么是程序计数器?

    程序计数器(Program Counter Register,程序计数寄存器):寄存器存储指令相关的现场信息。 CPU 只有把数据装栽到寄存器才能够运行。它是唯一一个在 Java虚拟机规范中没有规定任何 OutOfMemoryError 情况的区域。CPU 需要不停的切换各个线程,必然导致经常中断或恢复,JVM 的字节码解释器就需要通过改变 PC 寄存器的值来明确下一条应该执行的字节指令,各个线程之间便可以进行独立计算,从而不会出现相互干扰的情况。
    
  • JAVA栈与本地方法栈的区别?

    1. JAVA栈:当线程调用Java方法时,虚拟机创建栈针,进行压栈操作。
    2. 本地方法栈:当调用本地方法时,虚拟机保持JAVA栈不变(不再在Java栈中压入新的针),虚拟机直接动态链接调用指定的本地方法。如果某个虚拟机实现的本地方法接口是使用C连接模型的话,那么它的本地方法栈就是C栈。
    
  • 同一进程下,一个线程OOM,则其余线程可以正常执行吗?

    当一个线程抛出OOM异常后,它所占据的内存资源会全部被释放掉,从而不会影响其他线程的运行!
    即:共享区域堆是一个先增后减的曲线。
    
  • 如果主线程抛出异常退出了,子线程还能进行吗?

    线程不像进程,一个进程中的线程之间是没有父子之分的,都是平级关系。即线程都是一样的, 退出了一个不会影响另外一个。
    但是有一个例外情况,如果这些子线程都是守护线程,那么子线程会随着主线程结束而结束。
    
JVM如何清理垃圾对象?
JVM GC:Java虚拟机垃圾回收♻️,分为以下几种方式:
复制算法:
  • 适用于:针对新生代对象,朝生夕死的特点(对象留存少),采用复制的方式对内存进行整理。
  • HotSpot虚拟机默认Eden和Survivor大小的比例是8:1:1,也就是每次新生代中可用的内存空间(Eden)为整个新生代容量的80%,剩下的10%均分给Survivor,分为from(10%)和to(10%),两者交替往复,复制拷贝留存对象。即:每次GC都将Eden和其中一个survivor(from)中的存活对象复制到剩余的survivor(to)中。(IBM论文里说据他们统计95%的对象朝生夕死一样存活时间极短)
  • 缺点是:from和to始终有一个相当于是空闲的,10%内存空间的浪费。
  • 优点是:复制拷贝留存对象,后续一次性清理,无内存碎片产生。
标记-清除算法:
  • 对需要清理的对象进行标记,然后统一清理。
  • 适用于:针对老年代对象,留存高的特点,如果依然采用复制算法,需要拷贝移动的对象较多,效率低下,所以专注于需要被清理的对象(占少数),对其进行标记,然后统一清理。
  • 缺点是:标记-清除后,导致内存碎片产生,是空间上的一种浪费。
  • 优点是:仅标记需要被清理的对象,对留存高的情况十分友好,效率高。
标记-整理算法:
  • 标记-整理算法中,标记过程与标记-清除算法一样。但是在整理过程中,并不是简单的清理被标记的对象,而是让所有存活的对象统一向一端移动,然后直接清理掉边界以外的内存。
  • 缺点是:移动成本。
  • 优点是:避免了标记-清除中的碎片。
分代收集
  • 新生代:复制算法。老年代:标记-整理算法。
JVM如何判断一个对象是否需要被清理?(即:标记背后的逻辑)
  • 方法一:引用计数器,用于描述当前对象被引用的情况,值为0表示已无其他对象引用当前对象,则判定为可以被清理。缺点是,当两个无用对象A和B互相循环引用对方的时候,会导致引用计数器漏判的情况出现,A和B无法被及时清理。
  • 方法二:可达性分析算法,通过一系列被称为GC Roots的对象作为起点,沿着起点向下搜索,搜索走过的路径称为引用链。当一个对象到达GC Roots无任何引用链相连时,说明当前对象是无用的,可以考虑清理。GC Roots可以是:虚拟机栈中引用的对象。方法区中类静态属性引用的对象。方法区中常量引用的对象。本地方法栈中JNI(Native Method Stack)引用的对象。
内存充足都好说,如果内存不足,Java如何清理?
  • Java给对象的引用类型进行分类,进而划分清理的优先级。重要的对象不清理,不重要的对象内存不够再清理。无用的对象无论如何都清理。
Java引用类型有哪些?
  • StrongReference:Object object = new Object(),内存OOM也不会回收。(不回收

  • SoftReference:垃圾回收器在内存充足的时候不会回收。回收后将其加入到与之关联的引用队列中。非常适合于创建缓存

  • WeakReference:垃圾回收器在扫描到该对象时,无论内存充足与否,都会回收该对象的内存。回收后将其加入到与之关联的引用队列中。当 WeakHashMap 的键标记为过期时,这个键对应的条目就会自动被移除。避免了内存泄漏问题。被弱引用关联的对象只能生存到下一次垃圾收集发生之前。

  • PhantomReference:虚引用并不决定对象生命周期,如果一个对象只具有虚引用,那么它和没有任何引用一样,任何时候都可能被回收。虚引用主要用来跟踪对象被垃圾回收器回收的活动。与软引用和弱引用不同的是,虚引用必须关联一个引用队列。当垃圾回收器准备回收一个对象之前,如果发现它还具有虚引用,就会在对象回收前把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否加入了虚引用,来了解被引用的对象是否将要被回收,那么就可以在其被回收之前采取必要的行动。(标志提醒作用)**“为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知”。**所以虚引用更多的是用于对象回收的监听。系统gc监听:因为虚引用每次GC都会被回收,那么我们就可以通过虚引用来判断gc的频率,如果频率过大,内存使用可能存在问题,才导致了系统gc频繁调用。

  • 备注:内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。

CMS

  • CMS(Current Mark Swap):并发标记清除。

  • STW(Stop The World):在执行垃圾收集算法时,Java应用程序的其他所有除了垃圾收集收集器线程之外的线程都被挂起。由虚拟机在后台自动发起和自动完成的,是在用户不可见的情况下把用户正常工作的线程全部停下来。对于实时性要求很高的程序来说是难以接受

  • JVM如何优化?JNI如何优化?一次优化的DEMO?

JVM Optimize:Java虚拟机优化

逃逸分析
栈上替换
同步消除
标量替换
内联缓存

JMM

Java Memory Model(Java内存模型)

  • 什么是JMM(Java Memory Model)?

    Java内存模型是Java虚拟机中定义的一种并发编程的底层模型机制。
    主内存<->工作内存(缓存)<->线程
    
  • 什么是Happens-before?

    Happens-before定义: 如果一个操作 *Happens-before* 另一个操作,那么第一个操作的执行结果将对第二个操作可见,而且第一个操作的执行顺序在第二个操作之前。
    
    两个操作之间存在 Happens-before 关系,并不意味着必须按照代码指定顺序执行。如果重排序后的执行结果与按照 Happens-before 结果执行的结果一致,那也是可以的。
    
    happen-before:具有传递性(A happens before B,B happens before C,则A happens before C)
    
    总的来说,HB 原则是对单线程环境下的指令重排序以及多线程环境下的线程间数据的一致性进行的约束。单线程情况下保证串行语义,多线程情况下因为数据的一致性需要我们自己声明和保证,所以 JVM 自行保证了 HB 原则中提出的它认为必须要保证一致性的情况。
    
  • 什么是指令重排序?为什么要重排序?

    编译器、JVM 或者 CPU 都有可能出于优化等目的,对于实际指令执行的顺序进行调整,这就是重排序。
    
  • 什么是内存屏障/内存栅栏/内存栅障/屏障指令?

    1. 必要性:多数计算机要提高性能采取乱序执行,使得内存屏障成为必须。
      
    2. 作用:语义上,内存屏障之前的所有写操作都要写入内存,内存屏障之后的读操作,都可以获得屏障之前的写操作的结果。
    
    3. 硬件级别内存屏障:
    3.1 Load屏障,x86上的ifence指令,在其他指令前插入lfence指令,可以让高速缓存中的数据失效,强制当前线程从主内存同步加载数据。(内存同步到缓存,是从主内存装载)
    3.2 Store屏障,是x86的sfence指令,在其他指令后插入sfence指令,能让当前线程写入高速缓存中的最新数据更新写入主内存,让其他线程可见。(缓存刷新到内存,是存储到主内存)
    备注:在mfence(是一种全能型的屏障,具备ifence和sfence的能力)指令前的读写操作当必须在mfence指令后的读写操作前完成。
    
    4. 内存屏障:简单理解,指令卡点,告诉CPU和编译器,“我”是一道分水岭,之前的先于“我”之后的执行。同时,作为缓存同步卡点,强制更新不同缓存。
    4.1 确保特定操作执行顺序。
    4.2 影响数据可见性。
    4.3 在java里面有4种,就是 LoadLoad,StoreStore,LoadStore,StoreLoad,即:硬件级别内存屏障两两组合。
      	LoadLoad屏障:对于这样的语句Load1; LoadLoad; Load2,在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。(Load1读完才能读Load2StoreStore屏障:对于这样的语句Store1; StoreStore; Store2,在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见。(Store1写完才能写Store2LoadStore屏障:对于这样的语句Load1; LoadStore; Store2,在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕。(确保Load1的数据装载先于Store2及其后所有的存储指令刷新数据到内存的操作)
    		StoreLoad屏障:对于这样的语句Store1; StoreLoad; Load2,在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。它的开销是四种屏障中最大的。在大多数处理器的实现中,这个屏障是个万能屏障,兼具其它三种内存屏障的功能。
    
    5. 内存屏障与锁的性能比较:
    5.1 锁,内核在多个线程间干涉和调度,开销相对更大。
    5.2 内存屏障,编译器和CPU被禁止指令重排序(减少了优化),同时,刷新同步缓存,也有额外开销。
          
    6. 内存屏障的应用
    6.1 volatile:写volatile变量值之后,插入sfence。读volatile变量值之前,插入lfence。最终,保证可见性。
    6.2 final:编译器在final域的写之后,构造函数return之前,插入StoreStore屏障。在final域的读操作之前,加入LoadLoad屏障。
        在构造函数内对一个 final 域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。(保证初次写)
        初次读一个包含 final 域的对象的引用,与随后初次读这个 final 域,这两个操作之间不能重排序。(保证初次读)
    

Reference

  • https://blog.csdn.net/AmyZheng_/article/details/88790874(《深入理解Java虚拟机》读书笔记十)
  • https://blog.csdn.net/liuxiao723846/article/details/72779619(jvm:gc算法——复制、标记清除)
  • https://www.cnblogs.com/zhangxiaoguang/p/5792468.html(GC之详解CMS收集过程和日志分析)
  • https://blog.csdn.net/weixin_43884033/article/details/107739422(JVM 程序计数器的理解)
  • https://jingyan.baidu.com/article/63acb44a13717d61fcc17ed2.html(java本地方法)
  • https://www.cnblogs.com/hunrry/p/9183769.html(JVM方法栈的工作过程,方法栈和本地方法栈有什么区别)
  • https://blog.csdn.net/Fly_Fly_Zhang/article/details/89706328(JVM-JVM内存模型(Java Memory Model))
  • https://blog.csdn.net/zhaomengszu/article/details/80270696(java中JVM和JMM之间的区别)
  • https://blog.csdn.net/weixin_37841366/article/details/113086438(什么是指令重排序?为什么要重排序?)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值