Java Virtual Machine --JVM

A, JVM overall architecture

1, JVM (Java Virtual Machine): refers to the way the software simulates a complete hardware system function, run a full computer system completely isolated environment, physical machine software . The virtual machine has used VMWare, Virtual Box, Java Virtual Machine .

2, JVM consists of three major subsystems

  • Class loader subsystem (i.e., the class loader)
  • Runtime data areas (i.e., memory structures / memory model / JMM)
  • Execution Engine (includes garbage collector)

By the class loader .class files loaded into memory, then the execution engine to run.

Heap and method area is shared by all threads.

Java stack, native method stacks and program counter is not thread-sharing.

Two, JVM memory structure

The following example will be used to: define a class Math.

1, native method stacks (thread-private) : Registration native native method (ie native method is to keep native method stacks ), load the native method libraries (C language library) at the time of execution engines.

Example: Thread class START0 () method underlying implementation is written in C language.

2, the program counter (thread private) : is a pointer , the method directed to a method bytecode region ( used to store a pointer to the next instruction address, i.e. point instruction code to be executed next), read by execution engine instruction, is a very small memory space, almost negligible.

Example: The .class files javap -c decompiled, the figure below shows the results of decompilation Math () method: (iconst_1 and instructions representative of two istore_1 int a = 1)

3、方法区(线程共享):类的所有字段和方法字节码,以及一些特殊方法如构造函数、接口代码也定义在此(类加载器会将字节码文件加载到方法区中,分解成多个部分)。简单说,所有定义的方法的信息都保存在该区域,静态变量+常量+类信息(构造方法/接口定义)+运行时常量池都存在方法区中,虽然JVM规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做Non-Heap(非堆),目的应该是与Java堆区分开来。

4、Java栈(线程私有)Java线程执行方法的内存模型,一个线程对应一个栈,每个方法在执行的同时都会创建一个栈帧(用于存储局部变量表、操作数栈、动态链接、方法出口等信息),不存在垃圾回收问题,只要线程一结束该栈就释放,生命周期和线程一致。

JVM对该区域规范了两种异常:

① 线程请求的栈深度大于虚拟机栈所允许的深度时,将抛出StackOverFlowError异常。

② 若虚拟机栈可动态扩展,当无法申请到足够内存空间时将抛出OutOfMemoryError,通过JVM参数 -> Xss指定栈空间,空间大小决定函数调用的深度。

下图左边为Java栈的内存结构图:

动态链接:如Map map = new HashMap(),程序运行时map变量需要去找到运行时的HashMap实例对象(多态)的过程,就叫动态链接。

方法出口:如main()方法中的math()方法运行结束return返回的值用于main()方法中,return的这个过程就叫方法出口。

5、堆(线程共享): 虚拟机启动时创建,用于存放对象实例,几乎所有的对象(包含常量池)都放在堆上分配内存,当对象无法在该空间申请到内存时将抛出OutOfMemoryError异常。同时也是垃圾收集器管理的主要区域。可通过 -Xmx -Xms 参数来分别指定最大堆和最小堆。

下图为堆的内存结构图:

JVM调优的根本目的:尽量减少Full GC的次数,并且缩短每次Full GC的时间长度。(Full GC性能非常低,执行这个过程时会停止整个JVM,即STW(Stop The World),包括程序运行的那些线程,然后专门去做垃圾收集。现在新的GC会尽量缩短STW时间长度,尽量让垃圾收集和业务线程并发执行)

垃圾收集器(GC)存在的意义:回收堆中无引用的对象,释放空间

堆中新生代(Young Generation)占1/3的堆空间,新生代包括伊甸园区(Eden Space)和幸存者区(Survivor Space);老年代(Old Generation)占2/3的堆空间。

JDK1.8以前是没有元数据区(MetaData Space)的,而是永久代,从1.8开始元空间取代了永久代,本质和永久代类似,都是对JVM规范中方法区的实现,区别在于元数据区并不在虚拟机中,不属于堆中的内存结构,而是直接使用本机物理内存,永久代在虚拟机中,逻辑结构上属于堆,但是物理上不属于堆,堆大小=新生代+老年代。元数据区也有可能发生OutOfMemory异常。

  • JDK1.6及之前:有永久代,常量池在方法区
  • JDK1.7:有永久代,但已逐步“去永久代”,常量池在堆
  • JDK1.8及之后:无永久代,常量池在元空间

提问:为什么JDK1.8用元数据区取代了永久代?

官方解释:移除永久代是为融合HotSpot JVM与JRockit VM而做出的努力,因为JRockit没有永久代,不需要配置永久代。

(JDK 8实际上是这两种虚拟机合并之后的产物,HotSpot JVM(Sun公司开发),JRockit VM(BEA公司开发)。猜测:HotSpot JVM输了,没错,开发出Java语言的Sun公司最后输了,就听JRockit VM的)

new出来的对象会放在Eden区中,当Eden区占满时会做一次Minor GC(即轻GC),回收Eden区中无引用的对象,剩下的存活对象则会放到From区中。

② 后面又有新的new出来的对象不断存放在Eden区中,当Eden区占满时再做Minor GC,又有对象存放在From区中,当From区占满时也会做Minor GC,GC会回收Eden区和From区中无引用的对象,剩下的存活对象则会放到To区中,此时角色转变,From区变成To区,To区变成From区。

③ 接着,还会有新的对象存放到Eden区中,当Eden区占满时再做Minor GC,此时存活的对象放到新的From区中,当新的From区放满时又做Minor GC,把存活对象放入新的To区,依次轮循。

④ 如果一直有新的对象过来,不断的做Minor GC,当幸存者区的From区和To区轮循大概15次时,如果To区还有存活的对象,再做一次Minor GC则会把存活的对象移入老年代。

如果有一天老年代也占满了,则会触发Full GC再次回收无引用对象(有的GC会回收所有区,有的只会回收老年代,得看使用了哪种GC,后面会讲)。

三、执行引擎

执行引擎:读取运行时数据区的Java字节码并逐个执行。

解释执行字节码的方式:① JIT编译器;② 字节码解释器;③ mixed mode(前两种的混合模式,JDK 8是采用这种,之前的我不知道)

JIT编译器(Just In Time,即时编译):一次性解释所有指令,第一次执行慢,后面执行快。(会缓存)

字节码解释器:逐行解释指令,每次执行需要重新解释,前几次可能比JIT编译器快,后面比它慢。(不缓存)

 

Guess you like

Origin www.cnblogs.com/ZekiChen/p/12319401.html