一、JVM结构

1.Class Loader:依据特定格式,加载class文件到内存
2.Execution Engine:对加载的二进制字节码命令进行解析
3.Native Interface:融合不同开发语言的原生库为Java所用
4.Runtime Data Area:JVM内存空间结构模型
二、JVM内存空间结构模型
1.内存模型结构

(1) 线程私有
程序计数器(字节码指令)、虚拟机栈(java方法)、本地方法栈(native方法)
(2) 线程共享
MetaSpace、Java堆
2. 线程私有:程序计数器(Program Counter Register)
作用:
(1)线程独立拥有计数器,是当前线程所执行的字节码行号指示器(逻辑)
(2)改变计数器的值来选取当前线程需要执行的下一条字节码指令
(3)线程独有,即一个线程拥有一个计数器
(4)对Java方法计数,如果是Native方法则计数器值为Undefined
(5)不会发生内存泄漏
3. 线程私有:Java虚拟机栈
(1)作用:java方法执行的内存模型,包含多个栈帧
(2)程序执行过程:对于当前线程,每个方法会产生1个栈帧,当方法执行结束后,该栈帧取消。而每个栈帧中包含:局部变量表、操作栈、动态链接、返回地址等等。

(3) 当前栈中包含内容
局部变量表:包含了执行过程中的所有变量(boolen,char,string等等)
操作数栈:操作数栈:入栈、出栈、复制、交换、产生消费变量(4) 实例实现当前线程调用过程
测试类:MemoryCodeSample.java
package com.spring.ioc.c5;
public class MemoryCodeSample {
public static int add(int a,int b){
int c=0;
c=a+b;
return c;
}
}
- 编译:
src\main\java\com\spring\ioc\c5>javap -verbose MemoryCodeSample.class
- 反编译:
src\main\java\com\spring\ioc\c5>javap -verbose MemoryCodeSample.class
- 结果:
Classfile …/src/main/java/com/spring/ioc/c5/MemoryCodeSample.class
Last modified 2019-12-11; size 292 bytes
MD5 checksum 6d23ead3e45557a26587f2d70fbb9ee5
Compiled from "MemoryCodeSample.java"
public class com.spring.ioc.c5.MemoryCodeSample //描述类信息
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #3.#12 // java/lang/Object."<init>":()V
#2 = Class #13 // com/spring/ioc/c5/MemoryCodeSample
#3 = Class #14 // java/lang/Object
#4 = Utf8 <init>
#5 = Utf8 ()V
#6 = Utf8 Code
#7 = Utf8 LineNumberTable
#8 = Utf8 add
#9 = Utf8 (II)I
#10 = Utf8 SourceFile
#11 = Utf8 MemoryCodeSample.java
#12 = NameAndType #4:#5 // "<init>":()V
#13 = Utf8 com/spring/ioc/c5/MemoryCodeSample
#14 = Utf8 java/lang/Object
{
public com.spring.ioc.c5.MemoryCodeSample();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
public static int add(int, int);
descriptor: (II)I // 接受两个int输入,返回也是一个int
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=2 // 栈容量2,本地变量3,参数2个
0: iconst_0 // 把0压入操作数栈顶;同时获取输入的局部变量(1,2)
1: istore_2 // 把操作数栈顶元素取出,放入局部变量temp[2]位置
2: iload_0 // 把局部变量temp[0]压入操作数栈顶
3: iload_1 // 把局部变量temp[1]压入操作数栈顶
4: iadd // 把操作数栈中元素加和,压入操作数栈顶
5: istore_2 // 把操作数栈顶元素取出,放入局部变量temp[2]
6: iload_2 // 把局部变量temp[2]压入操作数栈中
7: ireturn // 把操作数栈顶元素返回
LineNumberTable:
line 6: 0 //代码第6行,对应字节码第0行
line 7: 2 //代码第7行,对应字节码第2行
line 8: 6 //代码第8行,对应字节码第6行
}
SourceFile: "MemoryCodeSample.java"
具体过程add(1+2)实例

4. 线程私有:本地方法栈
与虚拟机栈类似,主要用于标注native方法
5.线程共享:元空间(MetaSpace)
(1) 作用:存储class基本信息,包括java对象的method和field等
(2) 元空间使用的是本地内存,而永久代是使用jvm内存
(3) 元空间(MetaSpace)与永久代(PermGen)区别
在Java8中,元空间(Metaspace)登上舞台,方法区存在于元空间(Metaspace)。同时,元空间不再与堆连续,而且是存在于本地内存(Native memory)。

参考:https://www.cnblogs.com/secbro/p/11718987.html
6.线程共享:java堆
(1)堆内存分类:根据对象存活的周期不同,把堆内存划分为:新生代、老年代和永久代(对HotSpot虚拟机而言),这就是JVM的内存分代策略。
(2)作用:堆内存是虚拟机管理的内存中最大的一块,也是垃圾回收最频繁的一块区域,我们程序所有的对象实例都存放在堆内存中。
给堆内存分代是为了提高对象内存分配和垃圾回收的效率。试想一下,如果堆内存没有区域划分,所有的新创建的对象和生命周期很长的对象放在一起,随着程序的执行,堆内存需要频繁进行垃圾收集,而每次回收都要遍历所有的对象,遍历这些对象所花费的时间代价是巨大的,会严重影响我们的GC效率。
有了内存分代,新创建的对象会在新生代中分配内存;经过多次回收仍然存活下来的对象存放在老年代中;静态属性、类信息等存放在永久代中。新生代中的对象存活时间短,只需要在新生代区域中频繁进行GC;老年代中对象生命周期长,内存回收的频率相对较低,不需要频繁进行回收;永久代中回收效果太差,一般不进行垃圾回收。还可以根据不同年代的特点采用合适的垃圾收集算法。分代收集大大提升了收集效率,这些都是内存分代带来的好处。
(2)Java堆的分配区域
- JAVA1.8之前

参考:https://www.cnblogs.com/guanghe/p/10524314.html
- JAVA1.8之后

参考:https://blog.csdn.net/ztx114/article/details/79400789
四、问题
1.递归由于调度栈过深,导致超过虚拟机栈帧深度会引发java.lang.StackOverflowError异常
(1)代码:斐波那契数列
package com.spring.ioc.c5;
public class Fibonacci {
public static int fibonacci(int n){
if(n==0) return 0;
if(n==1) return 1;
return fibonacci(n-1)+fibonacci(n-2);
}
public static void main(String[] args) {
System.out.println(fibonacci(1));
System.out.println(fibonacci(2));
System.out.println(fibonacci(3));
System.out.println(fibonacci(4));
System.out.println(fibonacci(5));
System.out.println(fibonacci(1000000));
}
}
报错:
1
1
2
3
5
Exception in thread "main" java.lang.StackOverflowError
(2)解决:限制递归次数,或者使用循环替换递归
2.内存不够导致java.lang.OutOfMemoryError问题
(1)代码
package com.spring.ioc.c5;
public class stackLeak {
public void stackLeakByThread(){
while (true){
new Thread(){
public void run(){
while (true){
}
}
}.start();
}
}
}
(2)报错:(警告:绝对不建议尝试,可能引起死机)
Exception in thread "main"java.lang OutOfMemoryError: unable to create new native thread
来源:CSDN
作者:RayBreslin
链接:https://blog.csdn.net/u010886217/article/details/103497402
