JVM(Java虚拟机)原理及常见问题

1、JVM原理

     Java虚拟机(JVM),java源文件(.java)通过编译器生成字节码文件(.class),字节码文件(.class)通过JVM(Java虚拟机)中的解释器再翻译成特定机器上的机器码。 编译程序只需要面向虚拟机,生成虚拟机能够理解的代码,然后由解释器来将虚拟机代码转换为特定系统的机器码执行。每一种平台的解释器是不同的,但是实现的虚拟机是相同的。当一个程序从开始运行一个程序,这时虚拟机就开始实例化了。多个程序启动就会存在多个虚拟机实例。程序退出或者关闭。则虚拟机实例消亡。多个虚拟机实例之间数据不能共享。

引用:https://blog.csdn.net/wdjhzw/article/details/27720445

2、JVM体系结构

JVM的体系结构包含几个主要的子系统和内存区:

  1. 运行时数据区:又叫虚拟机内存或者Java内存,虚拟机运行时需要从整个计算机内存划分一块内存区域存储jvm需要用到的东西。而这个运行时数据区里面又会分为许多的小区。每个区都有自己不同的职责。
  2. 垃圾回收器(Garbage Collection, GC):负责回收堆内存(Heap)中没有被使用的对象,即这些对象已经没有被引用了。
  3. 类装载子系统:除了要定位和导入二进制class文件外,还必须负责验证被导入类的正确性,为类变量分配并初始化内存,以及帮助解析符号引用。
  4. 执行引擎:负责执行那些包含在被装载类的方法中的指令。     

3、Java运行时数据区(虚拟机内存)

引用:https://www.jianshu.com/p/e75094ee0744

1. 程序计数器

一块较小的内存空间,可以看做是当前线程所执行字节码的行号指示器,字节码解释器通过改变这个计数器的值选取下一条要执行的字节码指令,分支、循环、跳转异常处理、线程恢复都依赖指示器。每个线程都有一个独立的程序计数器。

2. 虚拟机栈

虚拟机栈是线程私有的,生命周期与线程相同,内存空间大小确定。虚拟机栈是描述Java方法执行的内存模型:每个方法执行的同时都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等。每个方法从调用直至执行完成的过程对应一个栈帧在虚拟机栈中入栈到出栈的过程。
局部变量表存储8种基本数据类型、对象的引用、returnAddress类型(指向一条字节码指令的地址)。

3. 本地方法栈

与虚拟机栈作用类似,服务于Native方法。

4. 堆

GC管理的主要区域,存放对象实例与数组,线程共享,虚拟机启动时创建,大小可扩展。因为虚拟机基本都使用分代收集算法,所以可以细分为新生代、老年代、持久代,再细致可以分为Eden空间、From Survivor空间、To Survivor空间。线程共享的堆空间可能划分出多个线程私有的分配缓冲区。Java堆可以处于物理上不连续的内存空间,只要逻辑上连续即可。

Java堆内存分配策略

  1. 对象优先在Eden分配
  2. 大对象(需要大量连续内存空间的Java对象)直接进入老年代
  3. 长期存活的对象将进入老年代

5. 方法区

线程共享,存放类信息、常量、静态变量、即时编译后的代码等数据。运行时常量池是方法区的一部分。

以上区域只有程序计数器不会OOM,其他区域当申请不到内存时抛出OOM异常。


4、 垃圾回收机制

引用:https://zhuanlan.zhihu.com/p/34759883

标记 - 清除算法

首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。它的主要不足有两个:一个是效率问题,标记和清除两个过程的效率都不高;另一个是空间问题,标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。

复制算法

为了解决效率问题,一种称为“复制”(Copying)的收集算法出现了,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。这样使得每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。只是这种算法的代价是将内存缩小为了原来的一半,未免太高了一点。

标记 - 整理算法

复制收集算法在对象存活率较高时就要进行较多的复制操作,效率将会变低。更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选用这种算法。根据老年代的特点,有人提出了另外一种“标记-整理”(Mark-Compact)算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。

分代收集算法
根据存活周期不同将内存划分为几块,根据各年代的特点采用最适当的收集算法。新生代使用复制算法,老年代使用标记-清理或标记-整理算法。

猜你喜欢

转载自blog.csdn.net/sinat_41144773/article/details/89021475