【Java虚拟机】JVM内存模型
JVM的内存模型
JVM的运行时内存主要分为Java虚拟机栈,堆,本地方法栈,程序计数器,元空间五个部分,还有一部分是直接内存,是操作系统的本地内存,也是可以直接操作的。
Java虚拟机栈
每个线程都有一个独立的虚拟机栈,并且在方法执行前都会在栈中创建一个栈帧,用来存储该方法的一些信息。
Java堆
堆是JVM中最大的一块区域,它是所有线程共享的,用来存储对对象的实例。每当遇到一个new关键字时,都会在堆中为其分配一块内存
本地方法栈
与虚拟机栈类似,本地方法栈是服务于本地方法的,当方法执行时也会创建一个栈帧。
程序计数器
程序计数器可以看作是Java程序运行字节码文件的行号指示器。它用来存储正在执行的Java方法的JVM指令地址。当执行本地方法时,它的值为null。
元空间
Java8以后,方法区被元空间所取代,它用来存放已将被虚拟机加载的类信息,常量,静态变量等信息。虽然它被称为堆的逻辑部分,但是还有“非堆”的别名,可以不实现垃圾回收
运行时常量池
运行时常量池是方法区的一部分,用于存放编译时产生的各种字面量和符号
直接内存
通过NIO类引入,极大提高了IO性能。直接内存的使用受到本机总内存的限制。
JVM内存模型里堆和栈有什么区别
- 用途不同:每当方法执行时都会在栈中创建一个栈帧,用来存放该方法的信息。而堆是用来存放实例化对象的。每当使用new关键字创建一个新对象时,都会在堆中为其分配一片内存
- 生命周期不同:栈中的栈帧有明确的生命周期,在方法被调用时被创建,方法运行结束后被清除。而堆中没有明确的生命周期,当垃圾回收器监测到该对象不再被引用时,会将该对象进行清除
- 空间不同:栈的空间相对较小,通常是不可变的。而堆的空间较大,而且是可扩展的
- 存取速度不同:栈采用的是先进后出的数据结构,操作简单,存取速度较快。而堆的存取速度较慢,并且垃圾回收也需要消耗一定的时间
- 可见性:栈是独立的,各个线程间是不可见的。而堆是被所有线程所共享的。
栈中存的是指针还是对象
存的是对象的引用
堆分为哪几部分
新生代
新生代又可以划分为伊甸区和幸存者区,幸存者区又可以划分为两个大小相等的区域,s0,s1。当一个新的对象被创建时通常首先会分配到伊甸区,当伊甸区满时会触发一次垃圾回收,在每次垃圾回收后都会将存活的对象移动到s0或s1。二者充当对象的中转站。帮助区分长期存活和短期存活的对象。
老年代
当经过多次垃圾回收后仍然存活的对象会被移动到老年代,这里的full GC频次较低,但是时间比较长。老年代的空间通常比新生代要大,用来存放更多长期存活的对象。
元空间
用来存放已将加载的类信息,常量,静态变量等信息。它不在堆内存中,而是在本地内存。解决了永久代的内存泄漏问题
大对象区
有些JVM的实现还有大对象区,用来专门存放放大对象
如果有一个大对象,一般存放在哪里
一般直接存放在老年代中,原因有两点
- 第一:新生代空间较小,如果直接存放大对象会导致内存不足,频繁出发垃圾回收,而垃圾回收涉及到对象的复制和转移,造成大量消耗。放入老年代可以减少新生代的压力。
- 第二:如果直接将大对象放入新生代,频繁将大对象进行分配和清理,会产生大量内存碎片。导致下次为大对象分配空间时找不到连续的大空间,造成内存溢出。而老年代空间较大,能够减少内存碎片的产生。
程序计数器为什么是私有的
Java程序允许多个线程同时运行。当线程1和线程2同时运行时,CPU就会为其分配时间片,当执行完线程1的时间片后代码还没有执行完毕,会先去执行线程2 的时间片。执行完毕后再回到线程一执行剩余代码。程序计数器就起到记录程序执行字节码指令地址的作用。因此是私有的
方法区中方法的执行过程
- 方法解析调用:首先JVM会根据方法的符号引用找到方法的实际地址
- 栈帧创建:执行方法前先在栈中创建栈帧用来存储该方法的相关信息
- 执行方法:执行方法内的字节码指令
- 处理返回结果:方法执行完毕后,可能会将执行结果返回给调用者。清除栈帧,回复调用者的执行环境
String保存在哪里
String保存在字符串常量池中,该对象是不可变的,被其他引用所共享
String s=new String(“abc”)涉及到哪些内存区域
首先我们看到new关键词,我们可以知道该对象是在堆内存中创建的。然后abc是被fianl所修饰的,我们会先去字符串常量池中去查询是否存在该字符串,如果存在直接返回引用,如果不存在会先在常量池中创建该字符串,并返回引用。因此分两种情况:
第一种情况:字符串常量池中存在,那么只涉及到一个对象的创建。
第二种情况:符串常量池中不存在,那么涉及到两个对象的创建。
引用的类型有哪些?有什么区别
- 强引用:指代码中普遍存在的赋值方式,这种引用永远不会被垃圾回收
- 软引用:指需要带不是必须的那些对象,当系统内存溢出前会将其进行垃圾回收
- 弱引用:比软引用还要低一点,当下一次垃圾回收时,一定会被回收掉
- 虚引用:最弱的引用,也被称为幻影引用。需要配合refenence queue使用。
弱引用可以用在哪里
弱引用通常用来创建非强制性对象引用,这些对象会在内存不足时自动进行垃圾回收。
使用场景:
- 实现缓存:通过弱引用实现缓存可以在JVM需要更多内存时自动清理掉这些缓存对象
- 对象池:使用弱引用管理这些不被使用的对象时,他们就可以被垃圾回收,释放内存
- 避免内存泄漏:当一个对象长期不被使用时,可以使用弱引用防止该对象长期存在,导致内存泄漏。