内存控制权利交给JVM-自动内存管理机制(内存分配、垃圾回收 内存泄漏、内存溢出)
堆Heap 线程共享
存放几乎所有对象实例以及数组,内存中最大的一块、JVM启动时创建
分代收集算法:Eden空间、From Survivor空间、To Survivor空间
内存分配:TLAB(Thread Local Allocation Buffer)
JVM规范:不需要物理上连续,逻辑上连续。
既可以是固定大小也可以是可扩展
方法区Method Area(非堆) 线程共享
1)存储已被虚拟机加载的类信息、静态变量、常量、即时编译器编译后的代码等数据
2)方法区里存放着类的版本,字段,方法,接口等描述信息和常量池。常量池里存储着字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池。
3)“永久代”:相对而言,垃圾收集行为在这个区域是比较少出现的,但并非数据进入方法区后就“永久存在”了。
运行时常量池:
1)用于存放编译期生成的各种字面量和符号引用,在常量池的符号引用有一部分是会被转变为直接引用的,例如类的静态方法或私有方法,实例构造方法,父类方法,这是因为这些方法不能被重写其他版本,所以能在加载的时候就可以将符号引用转变为直接引用,而其他的一些方法是在这个方法被第一次调用的时候才会将符号引用转变为直接引用的。
2)相对于Class文件常量池一重大特征就是具有动态性,Java并非要求常量一定只有编译期才能产生,即并不是先放入class文件中常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中,也就是String类的intern()方法。
字面量:Java语言层面常量的概念
文本字符串
final常量值
基本数据类型的值
其它
符号引用:编译原理方面的概念
类和接口的完全限定名
字段名称及描述符(属性)
方法名称及描述符(属性)
3)常量池是为了避免频繁的创建和销毁对象而影响系统性能,其实现了对象的共享。例如字符串常量池,在编译阶段就把所有的字符串文字放到一个常量池中。
- 节省内存空间:常量池中所有相同的字符串常量被合并,只占用一个空间。
- 节省运行时间:比较字符串时,==比equals()快。对于两个引用变量,只用==判断引用是否相等,也就可以判断实际值是否相等。
双等号==的含义
- 基本数据类型之间应用双等号,比较的是他们的数值。
- 复合数据类型(类)之间应用双等号,比较的是他们在内存中的存放地址。
总结:
运行时常量池是class文件常量池在运行时的表示。
运行时常量池里的内容除了是常量池里的内容外(符号引用的索引值是指向运行时常量池的),还将静态常量池里的符号引用转变为直接引用,而且动态常量池里的内容是能动态添加的。例如调用String的intern方法就能将String的值添加到String常量池中,这里String常量池是包含在运行时常量池里的,但在jdk1.8后,将String常量池放到了堆中。
那么,Integer i = 10是在运行时常量池里还是常量池里找呢?
虚拟机栈VM Stack 线程私有
Java方法执行的内存模型,由一个个栈帧组成
栈帧:
1)每一个方法从调用开始到执行完成的过程,就对应着一个栈帧在虚拟机栈里面从入栈到出栈的过程
2)用于支持虚拟机进行方法调用和方法执行的数据结构
3)包含局部变量表、操作数栈、动态链接、方法出口信息
局部变量表:方法参数和方法内部定义的局部变量
1)基本数据类型(boolean、byte、char、short、int、float、long、double)、reference类型(应用指针,句柄等)、returnAdress(指向了一条直接骂指令的地址)
2)局部变量表的容量以变量槽(Slot)为最小单位,32位虚拟机中一个Slot可以存放一个32位以内的数据类型,long\dubbo 2个Slot
3)局部变量表空间编译期间完成分配,运行期间不改边大小,方法的Code属性的max_locals数据项中确定了该方法所需要分配的最大局部变量表的容量
4)Slot是可以重用的,当Slot中的变量超出了作用域,那么下一次分配Slot的时候,将会覆盖原来的数据。Slot对对象的引用会影响GC(要是被引用,将不会被回收)
5)系统不会为局部变量赋予初始值
操作数栈:可理解为java虚拟机栈中的一个用于计算的临时数据存储区。
动态链接:
Java 是在运行期间动态链接的,所以为了支持动态链接,需要将方法区里面的符号引用转为直接引用(即:给出地址),这就叫动态链接。Class 文件中存放了大量的符号引用,字节码中的方法调用指令就是以常量池中指向方法的符号引用作为参数。这些符号引用一部分会在类加载阶段或第一次使用时转化为直接引用,这种转化称为静态解析。另一部分将在每一次运行期间转化为直接引用,这部分称为动态连接。
StackOverFlowError: 若Java虚拟机栈的内存大小不允许动态扩展,那么当线程请求栈的深度超过当前Java虚拟机栈的最大深度的时候,就抛出StackOverFlowError异常。
OutOfMemoryError: 若 Java 虚拟机栈的内存大小允许动态扩展,且当线程请求栈时内存用完了,无法再动态扩展了,此时抛出OutOfMemoryError异常。
本地方法栈Native VM Stack 线程私有
程序计数器PC 线程私有
1)较小的内存空间
2)当前线程所执行的字节码的行号指示器
3)作用
•字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理。
•在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候能够知道该线程上次运行到哪儿了。
直接内存 Direct Memory
非JVM运行时数据区,非JVM虚拟机规范,频繁使用,本机内存限制会OutOfMemoryError,NIO
https://wangwengcn.iteye.com/blog/1622195
https://blog.youkuaiyun.com/Jae_Wang/article/details/80291402