定义: 模拟某种计算机体系结构,执行特定指令集的软件
分类 系统虚拟机(VMware,Virtual Box等), 进程虚拟机java
特色:并不会完整的模拟一个操做系统的运行环境,仅仅提供了特定指令集的运行环境
实例: JVM, Adobe Flash, FC模拟器算法
特色:把特定指令集范围进一步限定为高级语言。
属于进程虚拟机的一种,若有 JVM, .NET CLR, P-Code缓存
可执行java语言的高级语言虚拟机。java语言虚拟机并不必定能够称为JVM, 例如:Apache Harmony数据结构
共有设计,私有实现
java虚拟机规范声明了概念模型,这些概念模型不约束虚拟机的具体实现,只要求虚拟机实现的效果在外部看起来和规范描述同样便可。
例如规范中规定了java堆中的对象所在的内存,须要虚拟机自动完成垃圾回收,这个实现过程能够不一样,可是效果须要相同。app
其中 :
方法区和堆是全部线程共享数据区。
虚拟机栈、本地方法栈、程序计数器 是线程私有的。jvm
在编译程序代码时,栈帧须要的局部变量表的大小和操做数栈的深度,在编译期间就已经彻底肯定了,java虚拟机会把这些数据彻底写在class文件的code表中。所以一个栈帧须要分配的内存大小不会受程序运行期间变量数据的影响,而仅仅取决于具体的java虚拟机。
在一个线程里面方法的调用链可能很长,不少方法可能都同时处于执行的状态,对于执行引擎而言,在活动线程之中,只有位于java虚拟机栈顶的栈帧才是有效的,这个栈帧被称为当前栈帧,与此栈帧相关联的方法被称为当前方法。虚拟机中全部执行的字节码指令都针对当前方法和当前栈帧操做。ide
定义: 是一组变量值的存储空间,用于存储方法,参数和方法内部定义的局部变量等等。
在java编译器编译class文件时,就已经肯定了该方法须要的局部变量表的最大容量,局部变量表是以槽(Slot)为最小单位的。java虚拟机规范中没有明确一个slot的具体占用的内存大小,只是描述了任何slot能够储存的类型。性能
在cmd中输入测试
copy con Test.java
而后输入程序:ui
public class Test{ public int calc(){ int a =100; int b =200; int c =300; return (a+b)*c; } }
而后编译
javac Test.java
再查看字节码:
javap -verbose Test.class
获得:
0: bipush 100 //把100入栈到操做数栈的栈顶 2: istore_1 //把操做数栈顶的元素出栈并把此元素存储在局部变量表中的1号Slot 3: sipush 200 6: istore_2 7: sipush 300 10: istore_3 11: iload_1 //将局变量表中的为1号的操做数入栈到操做数栈栈顶 12: iload_2 //将局变量表中的为2号的操做数入栈到操做数栈栈顶 13: iadd //将操做数栈栈顶的两个元素出栈,而后相加入栈 14: iload_3 15: imul 16: ireturn
注意: 在 idea中能够经过设置选项中的run --> Edit configuration --> configuration --> VM options 设置java虚拟机栈的大小 添加: -Xss128k
便可。
StackOverflowError异常
public class JavaVMStackSOF { private int stackLength = 1; public void stackLeak(){ stackLength++; stackLeak(); } public static void main(String[] args) { JavaVMStackSOF oom = new JavaVMStackSOF(); try{ oom.stackLeak(); }catch (Throwable e){ System.out.println("length" + oom.stackLength); throw e; } } }
OutOfMemoryError异常
public class JavaVMStackOOM { private void dontStop(){ while(true){ } } public void stackLeakThread(){ while(true){ Thread thread = new Thread(new Runnable() { @Override public void run() { dontStop(); } }); thread.start(); } } public static void main(String[] args) { JavaVMStackOOM oom = new JavaVMStackOOM(); oom.stackLeakThread(); } }
特征:
划分方式:
从栈到堆的关联过程:
第二种实现方式:
对比:
第二种方式中,reference中存储的是稳定的句柄的地址,在对象被移动时(对象常常被移动,垃圾回收时会发生移动),只会改变句柄池中的到对象类型数据的指针,而reference不会改变。
第一种方式,速度更快,对比使用句柄的方式,节省了指针开销,reference直接指向了对象的实例数据。
可能发生的异常:
出现OOM实例以下:
不断的建立对象,而且保证这些内存不被回收便可作到。
public class javaHeapOOM { static class OOMObject{ } public static void main(String[] args) { List<OOMObject> list = new ArrayList<>(); while(true){ list.add(new OOMObject()); } } }
注意:
类实例数据 和 类类型信息
实例数据是指在类中定义的各类实例对象以及它们的值,而类型信息是指定义在类代码中的常量,静态变量,类中声明的各类方法,字段等,还会包括即时编译器编译产生的数据。
永久代与方法区
永久代变迁过程致使的异常差别:
实例1:
public class RuntimeConstantPoolChange { public static void main(String[] args) { String str1 = new StringBuilder("hptj").append("zzj").toString(); System.out.println(str1.intern() == str1); String str2 = new StringBuilder("ja").append("va").toString(); System.out.println(str2.intern() == str2); } }
intern方法:按期的维护了一个字符串池,若是字符串中有这个字符串,则会返回这个池中常量的地址,若是这个字符串中没有这个字符串所需的常量,则jvm会先把这个字符串加到字符串常量池中,而后再返回这个字符串的地址。
可知:java1.7开始后字符串常量池被移到了Heap中,
当采用jdk1.6运行时,则出现 false false ,由于 str1.intern返回的是常量池中的地址,而str1本生的地址是在heap中的是不可能相同的。
当采用jdk1.7运行时,则出现 true false , 由于 str1.intern和heap都是堆中的地址,则这个hptjzzj字符串在heap中没有出现过,则会加入到heap中的常量池中,并返回此地址和str1在堆中的地址一致,而java字符串在堆常量池中已经存在,当new以后会新建一个对象,因此是不相等的。