内存模型
1.在JAVA虚拟机中管理的内存分为五个大区域
1.堆区:
2.方法区
堆区和方法区同属于线程共享的
3.虚拟机栈
4.本地方法栈
5.程序计数器
虚拟机栈,本地方法栈,程序计数器是属于线程独有的
可以用下图表示
作用
1.程序计数器
一块很小的内存空间,它是线程私有的,可以认作当前线程的行号指示器。也是JVM内存中唯一没有声明内存溢出异常的
行号指示器:例如如果我们有多个线程在执行,然后A线程执行到一半的执行权给了B线程,A线程再执行的时候是接着执行了的再执行还是重新开始执行呢,很显然,必然是接着执行再执行,那么再哪里查看行数指示器呢。在IDE工具中使用javap -v class文件名 进行测试
像这些0,1,2,3就是行号指示器
生命周期:从线程的开始到结束
2.虚拟机栈(Java栈)
就是我们经常说的栈,描述的是Java方法执行的内存模型(先进后出,后进先出)
先进后出,我们的main方法先开始执行,但因为里面可能有各种各样的操作,所以它就会最后才出
常见的错误
1.StackOverflowError:内存溢出
2.OutOfMemoryError:虚拟机空间可以动态扩展,但是当动态扩展的时候无法申请到足够的空间的时候,就抛出这个错误。我这里手动来改变栈的值
package mymemory;
public class Stack {
private static int index=1;
public void call(){
index++;
call();
}
public static void main(String[] args) {
Stack stack=new Stack();
try{
stack.call();
}catch (Throwable e){
e.printStackTrace();
}finally{
System.out.println("Stack deep:"+index);
}
}
}
修改值
运行
再次改变值
可以发现分配的内存越多,运行的次数也越多
3.本地方法栈
调用的是c或者c++语言编写的方法
例如Object的hashCode方法,这些本地方法的文件都在我们的Java的jre文件下面,以.dll结尾
本地方法栈和虚拟机栈作用非常相似,只不过一个是运行时压在本地栈,一个压在虚机机栈
4.堆(重要)
保存的是对象(new出来的对象)
具体包含
对象头包含
16字节,是64位系统的
无锁状态
GC标志
轻量级锁
…
对象的成员变量
成员方法
分为两个区域
1.新生代(3/1)
新生代又分为两个区域
1.Eden(8/10)伊甸园:对象最初诞生的区域,并且对大多对象来说,这里是它们唯一存在过的区域(因为大多数对象都是朝生夕生的)
2.Survivor Space(幸存者乐园):从伊甸园幸存下来的对象会被挪到这里来(经历过垃圾回收后还有用的对象),它又分两个区域(因为使用的是复制算法,需要两个区域)
1.From(1/10):有对象;优先放到From区,当From区放不下的时候就放到To
2.To(1/10):没有对象;To区放不下的时候又放到From区,就To,From一直相互放。如果程序计数器在对象的标记达到15次以后,就放到老年代
2.老年代(3/2):
老年代进入值的几种情况
1.就是上面To区放不下到From区一直放放放到15次的时候
2.当新创建的对象的值大于新生代内存的时候(大对象),直接放到老年代里面
5.方法区
和堆是一样的,是所有线程共享的内存区域,为了区分堆,也被称为非堆区,用于存储被JVM加载的类的信息。保存的是加载的类的信息,常量,静态变量,运行时常量池也是方法区的一部分