一、JVM的组成

1. JVM由那些部分组成,运行流程是什么? 

从图中可以看出 JVM 的主要组成部分

  •  ClassLoader(类加载器) 
  •  Runtime Data Area(运行时数据区,内存分区) 
  •  Execution Engine(执行引擎) 
  •  Native Method Library(本地库接口) 

 运行流程: 

  1.  类加载器(ClassLoader)把Java代码转换为字节码 
  2.  运行时数据区(Runtime Data Area)把字节码加载到内存中,而字节码文件只是JVM的一套指令集规范,并不能直接交给底层系统去执行,而是有执行引擎运行 
  3.  执行引擎(Execution Engine)将字节码翻译为底层系统指令,再交由CPU执行去执行,此时需要调用其他语言的本地库接口(Native Method Library)来
    实现整个程序的功能。 

 JVM 运行时数据区 

 组成部分:堆、方法区、栈、本地方法栈、程序计数器 

  1.  堆解决的是对象实例存储的问题,垃圾回收器管理的主要区域 
  2.  方法区可以认为是堆的一部分,用于存储已被虚拟机加载的信息,常量、静
    态变量、即时编译器编译后的代码。 
  3.  栈解决的是程序运行的问题,栈里面存的是栈帧,栈帧里面存的是局部变量
    表、操作数栈、动态链接、方法出口等信息。 
  4.  本地方法栈与栈功能相同,本地方法栈执行的是本地方法,一个Java调用非
    Java代码的接口。 
  5.  程序计数器(PC寄存器)程序计数器中存放的是当前线程所执行的字节码的
    行数。JVM工作时就是通过改变这个计数器的值来选取下一个需要执行的字节码
    指令。 

 什么是程序计数器? 

 线程私有的。内部保存的字节码的行号。用于记录正在执行的字节码指令的地址。 

 javap -verbose xx.class 打印堆栈大小,局部变量的数量和方法的参数。 

java虚拟机对于多线程是通过线程轮流切换并且分配线程执行时间。在任何的一个时间点上,一个处理器只会处理执行一个线程,如果当前被执行的这个线程它所分配的执行时间用完了【挂起】。处理器会切换到另外的一个线程上来进行执行。并且这个线程的执行时间用完了,接着处理器就会又来执行被挂起的这个线程。


那么现在有一个问题就是,当前处理器如何能够知道,对于这个被挂起的线程,它上一次执行到了哪里?那么这时就需要从程序计数器中来回去到当前的这个线程他上一次执行的行号,然后接着继续向下执行。
程序计数器是JVM规范中唯一一个没有规定出现OOM的区域,所以这个空间也不会进行GC

 

什么叫做Java堆

 

线程共享的区域。主要用来保存对象实例,数组等,当堆中没有内存空间可分配给实例,也无法再扩展时,则抛出OutOfMemoryError异常

 JAVA7中堆内会存在年轻代、老年代和方法区(永久代)


1Young区被划分为三部分,Eden区和两个大小严格相同的Survivor区,其
中,Survivor区间中,某一时刻只有其中一个是被使用的,另外一个留做垃圾收
集时复制对象用。在Eden区变满的时候, GC就会将存活的对象移到空闲的
Survivor区间中,根据JVM的策略,在经过几次垃圾收集后,任然存活于Survivor
的对象将被移动到Tenured区间。

2Tenured区主要保存生命周期长的对象,一般是一些老的对象,当一些对象
Young复制转移一定的次数以后,对象就会被转移到Tenured


3Perm代主要保存保存的类信息、静态变量、常量、编译后的代码,在
java7中堆上方法区会受到GC的管理的。方法区【永久代】是有一个大小的限制
的。如果大量的动态生成类,就会放入到方法区【永久代】,很容易造成OOM


为了避免方法区出现OOM,所以在java8中将堆上的方法区【永久代】给移动
到了本地内存上,重新开辟了一块空间,叫做元空间。那么现在就可以避免掉
OOM的出现了