一、什么是JVM虚拟机JVM是Java Virtual MachineJava虚拟机的缩写是一种用于计算机设备的规范它是一个虚构出来的计算机通过在实际计算机上仿真模拟各种计算机功能来实现的。只要计算机设备上安装了JVM就可以执行编译后的字节码文件.class并可跨平台使用。JVM的五大模块类装载器负责加载class文件运行时数据区执行引擎负责解释命令并提交操作系统执行本地方法接口融合不同的编程语言为Java所用通常为C/C垃圾收集器二、运行时数据区虚拟机栈每执行一个方法就会有该方法的一个栈帧入栈执行完出栈遵循“先入后出”主方法栈帧总是最后出栈本地方法栈本地方法库中的方法不是java代码堆存对象字符串常量池程序计数器元数据区方法区类信息“栈管运行 堆管存储”1、虚拟机栈每个java方法在被调用的时候就会创建一个栈帧并入栈一旦方法调用结束就出栈。其生命周期是随着线程的创建而创建随线程结束而释放是线程私有的因此不存在垃圾回收的问题。遵循“先进后出”原则。以一个java程序为例子解释一哈public class Test { public static void main(String[] args) { doit1(); doit3(); } public static void doit1() { doit2(); } public static void doit2() { //方法体 } public static void doit3() { //方法体 } }稍作解释一个java程序入口为主方法因此一定主方法的栈帧先入栈调用doit1()时doit1()栈帧入栈在doit1()中调用了doit2(),doit2()方法入栈执行完后出栈继续执行doit1()方法doit1()方法执行完毕出栈继续执行main方法main方法调用doit3()doit3栈帧入栈执行完后出栈继续main方法main方法执行完毕该程序执行完毕。栈帧栈帧中主要是局部变量表、操作数栈、动态链接、返回地址局部变量表主要存放byte、short、int、float、char、boolean、reference(指的是方法中的局部变量的对象引用)、returnAdress虚拟机数据类型单位是slotlong和double型变量占2个slot通过索引定位的方式使用局部变量表索引值范围从0开始在方法的执行过程中使用局部变量表完成参数值到参数列表的传递过程局部变量是没有初始值的操作数栈遵循“先进后出原则”方法的执行操作在操作数栈中完成每一个字节码指令往操作数栈进行写入和提取的过程就是入栈和出栈的过程操作数栈的每个元素是可是是任意的数据类32位数据类型所占栈容量为1,64位数据类型所占的栈容量是2当一个方法开始执行时这个方法的操作数栈数空的在方法的执行过程中通过一些字节码指令从局部变量表或者对象实例字段中复制常量或者变量值到操作数栈中当方法执行完后操作数栈为空。动态链接每个栈帧都包含一个执行运行时常量池JVM运行时数据区域中该栈帧所属方法的引用持有这个引用是为了支持方法调用过程中的动态链接在Class文件格式的常量池存储字面量和符号引用中存有大量的符号引用1、类的权限定名 2、字段名个属性 3、方法名和属性字节码中的方法调用指令就以常量池中指向方法的符号引用为参数。这些符号引用一部分会在类加载过程的解析阶段的时候转化为直接引用这里称为静态解析。另外一部分将在每一次的运行期间转换为直接引用这部分呢称为动态链接。方法返回地址当一个方法开始执行后只有两种方法可以退出这个方法。一执行引擎遇到任意一个方法的字节码指令这时候可能会有返回值传递给上层的方法调用者调用当前方法的方法称为调用者是否有返回值和返回值类型将根据遇到何种方法返回指令来决定这种退出方式称为正常完成出口二在方法的执行过程中遇到了异常并且这个异常没有在方法体内得到处理无论是java虚拟机内部产生的异常还是代码中使用抛出字节码指令而产生的异常只要在本方法中的异常处理器表中没有搜索到匹配的异常处理器就会导致方法退出这种退出称为异常完成出口该退出方式是不会给上层调用者任何返回值。无论采用哪种退出方式在方法退出后都需要返回到方法被调用的位置程序才能继续执行方法返回时可能需要在栈帧中保存一些信息用来帮助恢复它的上层方法的执行状态。一般来说方法正常退出时调用者的程序技术器的值可以作为返回地址栈帧中很可能会保存这个计数器值。而方法异常退出时返回地址是要通过异常处理器表来确定的栈帧中一般不保存这个信息、方法退出的过程实际上就等同于把当前栈帧出栈因此退出时可能执行的操作恢复上层方法的局部变量表和操作数栈把返回值如果有的话压入调用者栈帧的操作数栈中调整程序计数器的值以指向方法调用指令后面的一条指令等。均是参考的一个写的很好的简书https://www.jianshu.com/p/ecfcc9fb1de7当虚拟机栈满了之后会出现java.lang.StackOverflowErrorpublic static void main(String[] args) { doit(); } public static void doit() { doit(); }2、堆Java程序在运行时创建的所有类型对象个数组都存储在堆中。JVM会根据new指令在堆中开辟一个确定类型的的对象内存空间堆中开辟对象的空间并没有任何人工指令可以回收而是通过JVM的垃圾回收器负责回收。堆中对象存储的是该对象以及对象所在超类的实例数据但不是静态数据其中一个对象的引用可能在整个运行时数据区中的很多方法存在比如栈、堆、方法区等堆中对象还在关联一个对象的锁数据信息以及线程的等待集合线程等待池这些都是实现Java线程同步机制的基础java中数组也是对象在堆中会存储数组的信息堆内存如下新生代是new一个对象诞生、成长、消亡的区域一个对象在这里产生、应用最后被垃圾回收器收集结束生命。伊甸区所有的对象都在此被new出当伊甸区的空间用完而程序还在创建对象JVM的垃圾回收器对其进行垃圾回收Minor GC将伊甸区不再所引用的对象进行销毁然后将剩余的移动到幸存0区当幸存0区满了JVM垃圾回收器也会对其进行垃圾回收Minor GC移动到幸存1区依次类推。若老年代养老区满了JVM对其执行Full GC垃圾回收若依旧无法保存对象则会发生OOM异常OutOfMemoryError发生OOM异常的原因JAVA虚拟机的堆内存设置不够可以通过-Xmx、-Xms进行调整代码中创建了大量对象且长时间不能不能进行回收public static void main(String[] args){ long maxMemory Runtime.getRuntime().maxMemory();//返回JVM试图使用的最大内存量 long totalMemory Runtime.getRuntime().totalMemory();//返回JVM中的内存总量 System.out.println(MAX_MEMORY maxMemory (字节) (maxMemory/(double)1024/1024) MB); System.out.println(TOLAL_MEMORY totalMemory (字节) (totalMemory/(double)1024/1024) MB); } //输出 MAX_MEMORY 937951232(字节)894.5MB TOLAL_MEMORY 64487424(字节)61.5MB将堆内存调成800M-Xmx800m -Xms800m -XX:PrintGCDetails//输出 MAX_MEMORY 804257792(字节)767.0MB TOLAL_MEMORY 804257792(字节)767.0MB模拟下堆内存溢出OOM将对内存调成8M-Xmx8m -Xms8m -XX:PrintGCDetails执行如下代码public static void main(String[] args){ String str 模拟堆内存溢出; while(true) { str str new Random().nextInt(88888888) new Random().nextInt(999999999); } }则会出现OOM异常这是在执行上述代码时垃圾回收的一个过程3、垃圾回收机制VM参数-Xmx1024m -Xms1024m -XX:printGCDetails-Xms设置初始分配大小默认为物理内存的1/64-Xmx设置最大分配内存默认为物理内存的1/4-XX:printGCDetails输出详细GC处理日志GC频繁收集新生代较少收集老年代基本不动perm区Minor-GC普通GC只针对新生代Major-GC或者Full-GC针对老年代偶尔伴随对新生代的GC以及对永久代的GC一般是自动出发垃圾回收主要的垃圾回收算法复制算法标记-清除算法标记-整理算法分代收集算法新生代当对象在Eden包括一个Survior区域这里为from区出生后在经过一次Minor GC后如果对象还存活并且能被另外一块Survior区所容纳to区即to区有足够的内存空间存储Eden和from区的存活对象则使用复制算法将这些存活的对象复制到另一块Surviorto区然后清理所有使用的Eden以及from区并且将这些对象的年龄设置为1以后对象在survior区每熬过一次Minor GC就将其年龄加1当年龄达到某个值时一般为15这些对象就会成为老年代。Eden区的对象的存活率不高一般使用10%的内存作为空闲和活动区而另外80%的内存则用来给新new的对象分配内存一旦发生GC将10%的活动区间与另外80%中存活的对象转移到10%的空闲空间然后用90%的内存将被释放依次类推Edenfromto 8:1:1. 谁空谁为to区复制算法将内存分为两块每次只用其中一块当这块内存用完就将还活着的对象复制到另外一块。无论如何有一个区是空的谁空谁就是to区。优点不会产生碎片完整度高。缺点会浪费空间老年代一般是标记清除法和标记整理的混合实现标记清除算法从根集合开始扫描对存活的对象进行标记然后扫面整个内存空间回收未被标记的对象优点不需要内存空间缺点有碎片且要经历两次扫描耗时严重标记整理算法先标记活的对象然后整理使其内存连续再将未被标记的对象清除优点不会产生碎片缺点效率不高不仅要标记还有整理活着的对象的引用地址将其变为连续。从效率上讲该算法低于复制算法。分代收集算法各种算法的混合使用。未完待续。。。。。。。。。。。。
Java虚拟机入门知识以及面试常见题
一、什么是JVM虚拟机JVM是Java Virtual MachineJava虚拟机的缩写是一种用于计算机设备的规范它是一个虚构出来的计算机通过在实际计算机上仿真模拟各种计算机功能来实现的。只要计算机设备上安装了JVM就可以执行编译后的字节码文件.class并可跨平台使用。JVM的五大模块类装载器负责加载class文件运行时数据区执行引擎负责解释命令并提交操作系统执行本地方法接口融合不同的编程语言为Java所用通常为C/C垃圾收集器二、运行时数据区虚拟机栈每执行一个方法就会有该方法的一个栈帧入栈执行完出栈遵循“先入后出”主方法栈帧总是最后出栈本地方法栈本地方法库中的方法不是java代码堆存对象字符串常量池程序计数器元数据区方法区类信息“栈管运行 堆管存储”1、虚拟机栈每个java方法在被调用的时候就会创建一个栈帧并入栈一旦方法调用结束就出栈。其生命周期是随着线程的创建而创建随线程结束而释放是线程私有的因此不存在垃圾回收的问题。遵循“先进后出”原则。以一个java程序为例子解释一哈public class Test { public static void main(String[] args) { doit1(); doit3(); } public static void doit1() { doit2(); } public static void doit2() { //方法体 } public static void doit3() { //方法体 } }稍作解释一个java程序入口为主方法因此一定主方法的栈帧先入栈调用doit1()时doit1()栈帧入栈在doit1()中调用了doit2(),doit2()方法入栈执行完后出栈继续执行doit1()方法doit1()方法执行完毕出栈继续执行main方法main方法调用doit3()doit3栈帧入栈执行完后出栈继续main方法main方法执行完毕该程序执行完毕。栈帧栈帧中主要是局部变量表、操作数栈、动态链接、返回地址局部变量表主要存放byte、short、int、float、char、boolean、reference(指的是方法中的局部变量的对象引用)、returnAdress虚拟机数据类型单位是slotlong和double型变量占2个slot通过索引定位的方式使用局部变量表索引值范围从0开始在方法的执行过程中使用局部变量表完成参数值到参数列表的传递过程局部变量是没有初始值的操作数栈遵循“先进后出原则”方法的执行操作在操作数栈中完成每一个字节码指令往操作数栈进行写入和提取的过程就是入栈和出栈的过程操作数栈的每个元素是可是是任意的数据类32位数据类型所占栈容量为1,64位数据类型所占的栈容量是2当一个方法开始执行时这个方法的操作数栈数空的在方法的执行过程中通过一些字节码指令从局部变量表或者对象实例字段中复制常量或者变量值到操作数栈中当方法执行完后操作数栈为空。动态链接每个栈帧都包含一个执行运行时常量池JVM运行时数据区域中该栈帧所属方法的引用持有这个引用是为了支持方法调用过程中的动态链接在Class文件格式的常量池存储字面量和符号引用中存有大量的符号引用1、类的权限定名 2、字段名个属性 3、方法名和属性字节码中的方法调用指令就以常量池中指向方法的符号引用为参数。这些符号引用一部分会在类加载过程的解析阶段的时候转化为直接引用这里称为静态解析。另外一部分将在每一次的运行期间转换为直接引用这部分呢称为动态链接。方法返回地址当一个方法开始执行后只有两种方法可以退出这个方法。一执行引擎遇到任意一个方法的字节码指令这时候可能会有返回值传递给上层的方法调用者调用当前方法的方法称为调用者是否有返回值和返回值类型将根据遇到何种方法返回指令来决定这种退出方式称为正常完成出口二在方法的执行过程中遇到了异常并且这个异常没有在方法体内得到处理无论是java虚拟机内部产生的异常还是代码中使用抛出字节码指令而产生的异常只要在本方法中的异常处理器表中没有搜索到匹配的异常处理器就会导致方法退出这种退出称为异常完成出口该退出方式是不会给上层调用者任何返回值。无论采用哪种退出方式在方法退出后都需要返回到方法被调用的位置程序才能继续执行方法返回时可能需要在栈帧中保存一些信息用来帮助恢复它的上层方法的执行状态。一般来说方法正常退出时调用者的程序技术器的值可以作为返回地址栈帧中很可能会保存这个计数器值。而方法异常退出时返回地址是要通过异常处理器表来确定的栈帧中一般不保存这个信息、方法退出的过程实际上就等同于把当前栈帧出栈因此退出时可能执行的操作恢复上层方法的局部变量表和操作数栈把返回值如果有的话压入调用者栈帧的操作数栈中调整程序计数器的值以指向方法调用指令后面的一条指令等。均是参考的一个写的很好的简书https://www.jianshu.com/p/ecfcc9fb1de7当虚拟机栈满了之后会出现java.lang.StackOverflowErrorpublic static void main(String[] args) { doit(); } public static void doit() { doit(); }2、堆Java程序在运行时创建的所有类型对象个数组都存储在堆中。JVM会根据new指令在堆中开辟一个确定类型的的对象内存空间堆中开辟对象的空间并没有任何人工指令可以回收而是通过JVM的垃圾回收器负责回收。堆中对象存储的是该对象以及对象所在超类的实例数据但不是静态数据其中一个对象的引用可能在整个运行时数据区中的很多方法存在比如栈、堆、方法区等堆中对象还在关联一个对象的锁数据信息以及线程的等待集合线程等待池这些都是实现Java线程同步机制的基础java中数组也是对象在堆中会存储数组的信息堆内存如下新生代是new一个对象诞生、成长、消亡的区域一个对象在这里产生、应用最后被垃圾回收器收集结束生命。伊甸区所有的对象都在此被new出当伊甸区的空间用完而程序还在创建对象JVM的垃圾回收器对其进行垃圾回收Minor GC将伊甸区不再所引用的对象进行销毁然后将剩余的移动到幸存0区当幸存0区满了JVM垃圾回收器也会对其进行垃圾回收Minor GC移动到幸存1区依次类推。若老年代养老区满了JVM对其执行Full GC垃圾回收若依旧无法保存对象则会发生OOM异常OutOfMemoryError发生OOM异常的原因JAVA虚拟机的堆内存设置不够可以通过-Xmx、-Xms进行调整代码中创建了大量对象且长时间不能不能进行回收public static void main(String[] args){ long maxMemory Runtime.getRuntime().maxMemory();//返回JVM试图使用的最大内存量 long totalMemory Runtime.getRuntime().totalMemory();//返回JVM中的内存总量 System.out.println(MAX_MEMORY maxMemory (字节) (maxMemory/(double)1024/1024) MB); System.out.println(TOLAL_MEMORY totalMemory (字节) (totalMemory/(double)1024/1024) MB); } //输出 MAX_MEMORY 937951232(字节)894.5MB TOLAL_MEMORY 64487424(字节)61.5MB将堆内存调成800M-Xmx800m -Xms800m -XX:PrintGCDetails//输出 MAX_MEMORY 804257792(字节)767.0MB TOLAL_MEMORY 804257792(字节)767.0MB模拟下堆内存溢出OOM将对内存调成8M-Xmx8m -Xms8m -XX:PrintGCDetails执行如下代码public static void main(String[] args){ String str 模拟堆内存溢出; while(true) { str str new Random().nextInt(88888888) new Random().nextInt(999999999); } }则会出现OOM异常这是在执行上述代码时垃圾回收的一个过程3、垃圾回收机制VM参数-Xmx1024m -Xms1024m -XX:printGCDetails-Xms设置初始分配大小默认为物理内存的1/64-Xmx设置最大分配内存默认为物理内存的1/4-XX:printGCDetails输出详细GC处理日志GC频繁收集新生代较少收集老年代基本不动perm区Minor-GC普通GC只针对新生代Major-GC或者Full-GC针对老年代偶尔伴随对新生代的GC以及对永久代的GC一般是自动出发垃圾回收主要的垃圾回收算法复制算法标记-清除算法标记-整理算法分代收集算法新生代当对象在Eden包括一个Survior区域这里为from区出生后在经过一次Minor GC后如果对象还存活并且能被另外一块Survior区所容纳to区即to区有足够的内存空间存储Eden和from区的存活对象则使用复制算法将这些存活的对象复制到另一块Surviorto区然后清理所有使用的Eden以及from区并且将这些对象的年龄设置为1以后对象在survior区每熬过一次Minor GC就将其年龄加1当年龄达到某个值时一般为15这些对象就会成为老年代。Eden区的对象的存活率不高一般使用10%的内存作为空闲和活动区而另外80%的内存则用来给新new的对象分配内存一旦发生GC将10%的活动区间与另外80%中存活的对象转移到10%的空闲空间然后用90%的内存将被释放依次类推Edenfromto 8:1:1. 谁空谁为to区复制算法将内存分为两块每次只用其中一块当这块内存用完就将还活着的对象复制到另外一块。无论如何有一个区是空的谁空谁就是to区。优点不会产生碎片完整度高。缺点会浪费空间老年代一般是标记清除法和标记整理的混合实现标记清除算法从根集合开始扫描对存活的对象进行标记然后扫面整个内存空间回收未被标记的对象优点不需要内存空间缺点有碎片且要经历两次扫描耗时严重标记整理算法先标记活的对象然后整理使其内存连续再将未被标记的对象清除优点不会产生碎片缺点效率不高不仅要标记还有整理活着的对象的引用地址将其变为连续。从效率上讲该算法低于复制算法。分代收集算法各种算法的混合使用。未完待续。。。。。。。。。。。。