JVM基本结构图:
类加载子系统:负责才能够文件系统或者网络中加载class信息,加载的信息存放在方法区。
方法区:存放类信息,常量,常量池,静态常量等
堆:最主要的java工作内存区域,几乎所有的对象实例都存放在java堆中。常见的堆的结构如下
分为新生代区(eden,s0,s1 (s0,s1也叫做from和to区))
老年代区 tenured
栈:存放局部变量以及自定义对象的引用
程序计数器(和系统相关)
本地方法栈:和java栈很相似,最大不同本地方法栈用于本地方法调用。
垃圾收集器:java有一套属于自己的垃圾回收机制,用于内存的回收,无需开发人员手动清理。后面会讲到
执行引擎:执行虚拟机的字节码,一般先编译成机器码后执行。
堆内存详解:
根据回收机制的不同,堆的结构可能不同,最为常见的是把堆分为新生代和老年代,
其中新生代存放刚创建的对象或者年龄不大的对象,老年代则存放老年代对象。
新生代分为eden区,s0区,s1区(s0区,s1区也叫做from区和to区,他们是两块大小相等并且角色可以互换的空间)
绝大数情况下,对象会分配在Eden区,在一次新生代回收以后,如果对象还存活,就进入s0或者s1区,之后每经过一次新生代回收以后,对象的年龄就会加1,达到一定的年龄以后,就会进入老年代。
我们创建一个对象都做了什么?
比如 User u1 = new User();
我们创建类的信息,User类的一些信息是存在方法区的(类信息以及静态信息)
而u1 被实例化出来以后是存放在堆中的,当我们去使用都是使用u1的引用,这个u1是存在栈中的
内存分配流程
1. JVM会试图为相关Java对象在Eden中初始化一块内存区域
2. 当Eden空间足够时,内存申请结束。否则到下一步
3. JVM试图释放在Eden中所有不活跃的对象, 释放后若Eden空间仍然不足以放入新对象,则试图将部分Eden中活跃对象放入Survivor区
4. Survivor区被用来作为Eden及OLD的中间交换区域,当OLD区空间足够时,Survivor区的对象会被移到Old区,否则会被保留在Survivor区
5. 当OLD区空间不够时,JVM会在OLD区进行完全的垃圾收集
6 完全垃圾收集后,若Survivor及OLD区仍然无法存放从Eden复制过来的部分对象,导致JVM无法在Eden区为新对象创建内存区域,则出现”out of memory错误”