生命周期起点是当一个java应用main函数启动时虚拟机也同时被启动,而只有当在虚拟机实例中的所有非守护进程都结束时,java虚拟机实例才结束生命。
main函数就是一个java应用的入口,main函数被执行时,java虚拟机就启动了。启动了几个main函数就启动了几个java应用,同时也启动了几个java的虚拟机。
一种叫守护线程,一种叫非守护线程(也叫普通线程),main函数就是个非守护线程,虚拟机的gc就是一个守护线程。java的虚拟机中,只要有任何非守护线程还没有结束,java虚拟机的实例都不会退出,所以即使main函数这个非守护线程退出,但是由于在main函数中启动的匿名线程也是非守护线程,它还没有结束,所以jvm没办法退出
public class MianAndThread{
public static void main( String args[]){
new Thread(new Runnable(){
@override
public void run(){
Thread.currendThread.sleep(5000s);
System.out.println("睡了5s后打印,这是出main之外的非守护线程,这个推出后这个引用结束,jvm声明周期结束。任务管理的java/javaw.exe进程结束"
}
}
System.out.println("mian线程直接打印,mian线程结束,电脑任务管理器的java/javaw.exe进程并没有结束。")
}
}
类元信息(类元信息在类编译期间放入方法区,里面放置了类的基本信息,包括类的版本、字段、方法、接口以及常量池表)
常量池表存储了类在编译期间生成的字面量、符号引用,这些信息在类加载完后会被解析到运行时常量池中
运行时常量池主要存放在类加载后被解析的字面量与符号引用,但不止这些
运行时常量池具备动态性,可以添加数据,比较多的使用就是String类的intern()方法
在该区内很少发生垃圾回收,但是并不代表不发生GC,在这里进行的GC主要是对方法区里的常量池和对类型的卸载
在虚拟机启动时创建,几乎所有的对象实例都在这里创建,因此java堆的空间也是最大的。如果java堆空间不足了,程序会抛出OutOfMemoryError异常。
该区域经常发生垃圾回收操作。
在 JDK 7 版本及 JDK 7 版本之前,堆内存被通常分为下面三部分:
新生代内存(Young Generation)
老生代(Old Generation)
永久代(Permanent Generation)
下图所示的 Eden 区、两个 Survivor 区 S0 和 S1 都属于新生代,中间一层属于老年代,最下面一层属于永久代。
JDK 8 版本之后 PermGen(永久) 已被 Metaspace(元空间) 取代,元空间使用的是直接内存。
虚拟机栈是线程私有的,随线程生灭。虚拟机栈描述的是线程中的方法的内存模型:
每个方法被执行的时候,都会在虚拟机栈中同步创建一个栈帧。方法被执行时入栈,执行完后出栈。
每个栈帧的包含如下的内容
虚拟机栈可能会抛出两种异常:
如果线程请求的栈深度大于虚拟机所规定的栈深度,则会抛出StackOverFlowError即栈溢出(递归很深就会造成栈溢出)
如果虚拟机的栈容量可以动态扩展,那么当虚拟机栈申请不到内存时会抛出OutOfMemoryError即OOM内存溢出
本地方法栈和虚拟机栈类似,只不过本地方法栈为Native方法服务。
在任何时刻,一个处理器内核只能运行一个线程,多线程是通过线程轮流切换,分配时间来完成的,这就需要有一个标志来记住每个线程执行到了哪里,这里便需要到了程序计数器。
所以,程序计数器是线程私有的,每个线程都已自己的程序计数器。
程序计数器是唯一一个不会出现 OutOfMemoryError 的内存区域,它的生命周期随着线程的创建而创建,随着线程的结束而死亡。
方法区存放了类的信息,有类的静态变量、final类型变量、field自动信息、方法信息,处理逻辑的指令集,我们仔细想想一个类里面也就这些东西,而堆中存放是对象和数组,咋一看好像方法区跟堆的作用是一样的。其实呢,1,这里就关系到我们平时说的对象是类的实例,是不是有点恍然大悟了?这里的对应关系就是 “方法区–类” “堆–对象”,以“人”为例就是,堆里面放的是你这个“实实在在的人,有血有肉的”,而方法区中存放的是描述你的文字信息,如“你的名字,身高,体重,还有你的行为,如吃饭,走路等”。2,再者我们从另一个角度理解,就是从前我们得知方法区中的类是唯一的,同步的。但是我们在代码中往往同一个类会new几次,也就是有多个实例,既然有多个实例,那么在堆中就会分配多个实例空间内存。
*启动一个jvm虚拟机程序就是启动了一个进程。启动的同时就在操作系统的堆内存中开辟一块jvm内存区
虚拟机栈、本地方法栈、程序计数器这三个模块是线程私有的,有多少线程就有多少个这三个模块,声明周期跟所属线程的声明周期一致。以程序计数器为例,因为多线程是通过线程轮流切换和分配执行时间来实现,所以当线程切回到正确执行位置,每个线程都有独立的程序技术器,各个线程之间的计数器互不影响,独立存储。
其余是跟JVM虚拟机的生命周期一致。
方法区的内存空间不能满足内存分配需要时,将抛出OutOfMemoryError异常
java栈空间不足了,程序会抛出StackOverflowError异常
java堆空间不足了,程序会抛出OutOfMemoryError异常
程序计数器模块是JVM内存区域唯一不会报outofMemoryError情况的区域
类常量池与运行时常量池都存储在方法区,而字符串常量池在jdk7时就已经从方法区迁移到了java堆中。
在类编译过程中,会把类元信息放到方法区,类元信息的其中一部分便是类常量池,主要存放字面量和符号引用,而字面量的一部分便是文本字符,在类加载时将字面量和符号引用解析为直接引用存储在运行时常量池;
对于文本字符来说,它们会在解析时查找字符串常量池,查出这个文本字符对应的字符串对象的直接引用,将直接引用存储在运行时常量池;字符串常量池存储的是字符串对象的引用,而不是字符串本身。
参考:https://zhuanlan.zhihu.com/p/428694393