JVM
基础结构 JVM
内部结构如下:栈、堆。
JVM
中的栈主要是指线程里面的栈,里面有方法栈、 native
方法栈、 PC
寄存器等等;每个方法栈是由栈帧组成的;每个栈帧是由局部变量表、操作数栈等组成。
每个栈帧其实就代表一个方法
java
中所有对象都在堆中分配;堆中对象又分为年轻代、老年代等等,不同代的对象使用不同垃圾回收算法。
-XMs
:启动虚拟机预留的内存
-Xmx
:最大的堆内存
根据研究表明,堆中对象大部分都是创建后,立马就可以被销毁的。如:
为了优化堆中的内存,将堆中对象分为不同代。在年轻代中, GC
发生比较频繁;在老年代中, GC
发生比较少。
Young Generation Old Generation/Tenured Permanent Generation
永久代在 Java
虚拟机规范中是没有的,但是 Host Spot
虚拟机中有。
方法区被所有线程共享;方法区是用来存储编译后的代码,即存储每个类的运行时常量池、字段和方法。
方法区在虚拟机启动时创建;虽然方法区在逻辑上是堆的一部分,但在一些简单的实现中,方法区可以选择不进行垃圾回收和紧凑化。
方法区在 java8
的变化
java7
之前:方法区的实现:永久代,是作为堆的一部分; java8
之后:方法区的实现: metaspace
,是堆外的内存; 1、为什么要这样改变?
因为 java
可以动态加载字节码信息,这样方法区就会慢慢的挤占堆中内存。为了避免与堆争抢内存, java8
将方法区的实现移至堆外。
2、方法区、永久代、 MetaSpace
的区别?
方法区是 java
虚拟机规范所规定的一个概念。其中 java7
实现方法区的地方称为永久代; java8
实现方法区的地方称为 MetaSpace
java
程序在运行的时候,将源码编译成字节码,字节码在不同系统上的 JVM
翻译成对应的机器码。这是 Java平台无关性的基础 。
但是,编译后的字节码是如何读取到 JVM
中的?字节码执行引擎是如何识别、执行指令?
1、如何查看字节码文件
classpy
工具 IDEA
的 jclasslib Bytecode viewer
插件 2、字节码文件结构
一个字节码文件包含以下部分:
(1)magic:0xCAFEBABE
class
文件的 magic code
,用于标识该文件是 class
文件。
(2) minor_version
、 major_version
用于标识该 class
文件的版本,防止高版本的 class
文件被低版本的 JVM
读取并执行。
(3) constant_pool
:常量池
用于存储该 class
文件经常被使用的信息,优化内存。比如说 System.out.print()
(4) access_flag
表示这个类得访问权限,对应到 java
源码就是 public
、 final
之类的
这里以一个线程为例。一般来说,一个方法栈最底层的栈帧都是 Thread.run
方法。当一个线程准备调用另一个方法时,会先将实参拷贝一份到新栈帧的局部变量表里,然后再执行代码。
1、局部变量表
每次调用新方法时,会默认将当前对象的地址 this
作为局部变量表的第一个参数;后面存放传过来的参数。这与 javascript
的做法很相似。
2、方法调用的相关指令
invokevirtual
:一般实例方法,有多态; invokeinterface
:接口方法,有多态 invokestatuc
:静态方法,无多态 invokespecial
:特殊方法,无多态 invokedynamic
:动态调用, JDK7
新增,方法无需在编译时确定 3、方法调用的过程
(1)在开始时
this
(2)在返回时
(3)在异常出现时
(4)在 finally
时
4、为什么 Mockito
、 EasyMock
无法对 private
、 static
方法进行 mock
?
因为他们 mock
方法是通过覆盖这些方法来实现的,而 private
、 static
没法被覆盖。 PowerMock
是通过修改字节码文件达到 mock
私有、静态方法的。
原博客地址