优秀的编程知识分享平台

网站首页 > 技术文章 正文

谈谈JMM与JVM的相关知识(jmm jvm juc)

nanyue 2024-10-28 16:42:17 技术文章 8 ℃

我们在面试的时候,经常会被问到一些关于内存的问题,或许有很多小伙伴就在JMMJVM之间犯迷糊i,今天就来聊聊这两者的相关内容。

JMM: java memory model 翻译过来就是Java 内存模型。

为什么会有这个东西呢?

在操作系统层面,cpu的执行速度是很快的,而磁盘的读取速度很慢,由于磁盘的io操作涉及到磁盘的寻址,寻道加数据传输(io操作不是本文重点,下次再讲)很耗性能。因此cpu读取磁盘上的数据的性能瓶颈就是io,目前基于高速缓存的存储交互很好的解决了cpu和内存等其他硬件之间的速度矛盾,多核情况下各个处理器(核)都要遵循一定的诸如MSI、MESI等协议来保证内存的各个处理器高速缓存和主内存的数据的一致性


cpu不会直接去操作磁盘等硬件,而是操作高速缓存,这样性能上是提升上来了,但是在多cpu情况下,就会出现数据安全问题,例如:两个线程同时修改主内存中的一个值X,过程是线程a先从主内存将X的值读到a的工作内存,当a要对X进行+1操作时,突然a的cpu时间到了,切换到线程b,b也是和a一样的操作,然后切换到a将X+1的值更新到主内存,然后b再将X+1的值更新到主内存,本来在主内存中是X+2,但是实际上却是X+1,此时就发生了线程不安全的情况。也就是我们所说的X+1不是一个原子操作,X的值的变更对线程来说是不可见的。在java中可以通过用volate修饰变量来达到可见性。下期讲volate用法。

JVM: Java Virtual Machine,是java虚拟机的一个数据结构,java程序都是运行在虚拟机上。

可以理解是java运行时数据区域。


从上图可以看出,JVM由堆、虚拟机栈、本地方法栈、程序计数器、方法区组成

堆:jvm管理的最大的一块内存区域,所有线程共享堆数据

  1. 存储内容:new创建的对象和数组,垃圾收集器就是收集这些对象,然后根据GC算法回收
  2. 区域分配:分为eden区,s0区也叫from区,s1区也叫to区,老年代
  3. 堆的大小可以通过JVM选项-Xms和-Xmx来进行调整

分配比例:

Eden区 —— 新对象或者生命周期很短的对象会存储在这个区域中,这个区的大小可以通过-XX:NewSize-XX:MaxNewSize参数来调整

Survivor区 —— 那些历经了Eden区的垃圾回收仍能存活下来的依旧存在引用的对象会待在这个区域。这个区的大小可以由JVM参数-XX:SurvivorRatio来进行调节。

老年代 —— 那些在历经了Eden区和Survivor区的多次GC后仍然存活下来的对象(当然了,是拜那些挥之不去的引用所赐)会存储在这个区里。这个区会由一个特殊的垃圾回收器来负责。年老代中的对象的回收是由老年代的GC(major GC)来进行的。

新生代gc:Minor GC,gc算法是复制算法(从from区复制到to区)。

MinorGC的过程:采用复制算法

  1. 首先,把Eden和ServivorFrom区域中存活的对象复制到ServicorTo区域(如果有对象的年龄以及达到了老年的标准,一般是15,则赋值到老年代区)
  2. 同时把这些对象的年龄+1(如果ServicorTo不够位置了就放到老年区)
  3. 然后,清空Eden和ServicorFrom中的对象;最后,ServicorTo和ServicorFrom互换,原ServicorTo成为下一次GC时的ServicorFrom区

老年代gc:MajorGC 标记—清除算法

  1. 首先扫描一次所有老年代,标记出存活的对象
  2. 然后回收没有标记的对象。

MajorGC的耗时比较长,因为要扫描再回收。MajorGC会产生内存碎片,为了减少内存损耗,我们一般需要进行合并或者标记出来方便下次直接分配。

当老年代也满了装不下的时候,就会抛出OOM(Out of Memory)异常。

堆是最容易发生内存溢出和内存泄漏;

内存溢出:新创建的对象在堆中申请内存空间,内存空间不够时发生gc,gc后还是内存不够,就会发生内存溢出

内存泄漏需要被gc的对象没有及时清除,在需要给对象申请内存空间时空间不足,就会发生内存泄漏

栈:栈分为本地方法栈和虚拟机栈

本地方法栈是调用一些底层的native方法。

虚拟机栈是线程私有的,其基本单元是栈帧,每个方法代表一个栈帧,存储的是一些据补变量表,操作涉数栈、动态链接、方法出口等,栈是一个先进后出的数据结构,一个方法的调用表示压栈,方法结束后出栈,有些递归的方法,如果没有出口或者递归层数很深,会引发栈内存溢出。可以通过JVM选项

-Xss来进行调整

程序计数器:是记录代码执行的行号,通过字节码执行引擎来对行号进行修改,如果执行的方法是native,则计数器为undinfined,

方法区:jdk1.7也叫永久代,jdk1.8则是将元空间代替了永久代,因为永久代用的是jvm内存,不可动态调整,而且会占用jvm内存,而在1.8用元空间代替,元空间则是一块物理内存区域,可通过增加物理内存来调整。

Tags:

最近发表
标签列表