《Java 底层原理》Java内存模型
前言
通过JDK原理来重新理解一遍Java内存模型,通过其他方式学习到,总会有错误或者遗漏的情况。
内存模型
Java常常被提到的4个概念:
class文件:硬盘上的.class文件
class content:类加载器将.clas文件加载入内存,存储字节码文件数据的那块内存区域
Class对象:Class clazz = Test.class;
Java对象 new关键字或者其他方式产生的实体对象:例如 Test obj = new Test();
Java内存结构图:
1. 方法区:JDK8及之后通过元空间实现,元空间使用的OS内存,存放的内容为:类名,方法名,属性名,变量名,static 等等
2. 本地方法栈:JNI,Java调用c++方法的链接库,Java native方法的调用。
3. 虚拟机栈:一个线程一个虚拟机栈,一个虚拟机栈里面方法调用的次数个栈帧。
栈帧包含:
局部变量:方法里面的变量信息(int a = 0; int b = 1 这类信息的a 和 b 信息记录的地方)。
操作数栈:方法里面的 a+b , a = 4,等等这类操作的信息。
动态链接:mian方法对应的Jvm对象在元空间的内存地址。
返回地址:保存现场,保存上一个方法的调用完这个方法(test)的下一个程序计数器,保证后续方法执行完成之后,后面的代码可以继续执行。
附加信息:
JVM运行test方法,内部是怎么做的?
第一步、创建test的方法的栈帧
第二步、在test方法的栈帧中保存上一个方法的字节码的下一行程序计数器(比如:21)
第三步、线程的局部表开始指针(上一个方法的)保存至add方法的栈帧
第四步、线程的操作数栈开始指针(上一个方法的)保存至add方法的栈帧, 1,2,3这3步表示保存现场。
第五步、将test方法的局部表指针赋值给线程的局部表指针
第六步、将test方法的操作数栈指针赋值给线程的操作数栈指针
4. 程序计数器:字节码的索引(EIP,RIP)
Java 各个内存之间的引用关系
- 虚拟机栈指向方法区:动态链接,例如:test.mian()方法。
- 虚拟机栈指向堆区:对象的引用变量,例如:Test obj = new Test();
- 方法区指向堆区:引用类型的静态属性,例如static Test = new Test(),Test在堆区,static 在方法区。
- 堆区指向方法区:对象的内存布局Klass的信息部分存在方法区。
Jvm 源码中的内存分布:(Cheap,ValueObj,AllStatic)
class CHeapObj { public: void* operator new(size_t size) throw(); void operator delete(void* p); void* new_array(size_t size); }; // Base class for objects used as value objects. // Calling new or delete will result in fatal error. class ValueObj { public: void* operator new(size_t size) throw(); void operator delete(void* p); }; // Base class for classes that constitute name spaces. class AllStatic { public: void* operator new(size_t size) throw(); void operator delete(void* p); };
总结
学习内存结构,就是为了解决我们工作的问题。