JVM内存区域分配
堆
Java堆是各线程共享的内存区域,在jvm启动时创建,这块区域时jvm中最大的,用于存储应用的对象和数组,也是GC主要的回收区,一个jvm实例只存在一个堆内存,堆内存是可以调节的,类加载器读取类文件后,需要把类、方法、常量放到堆内存中,以方便执行器执行,堆内存分为三部分:新生代、老年代、永久代。
栈
Java线程栈私有的,是在线程创建时创建,它的生命周期是跟随线程的生命期,线程结束栈内存也就释放,对于栈来说不存在垃圾回收问题,只要线程一结束该栈就Over,生命周期和线程一致,基本类型的变量和对象的引用变量都是在函数的栈内存中分配。
栈帧
每个方法执行都会创建一个栈帧。每个方法都有独立的栈帧内存空间,栈数据结构:先进后出销毁
栈帧内部细节结构:局部变量表、操作数栈、动态链接、方法出口。
- 每个运行时所需要的内存,称作为虚拟机栈
- 每个栈有多个栈帧组成,对应着每次方法调用时占用的内存
- 每个线程只能有一个活动的栈,对应着当前正在执行的方法。
程序计数器
记录当前线程执行下一行指令的执行地址,只有在多线程的时候才起作用。
本地方法栈
本地方法栈和jvm栈发挥的作用非常相似,也是线程私有的,区别是jvm栈为jvm执行Java方法,而本地方法栈为jvm使用到的Native方法服务,是java调用c语言方法。
栈内存溢出
栈帧没有及时释放,导致栈空间满了,导致栈内存溢出。
方法区(元空间)
常量池
静态常量池、运行常量池、字符串常量池
存放区域
- java6和6之前,常量池是存放在方法区中的(永久区,gc不会回收)
- java7,将常量池存放到堆中(不合理,gc会回收,有一些全局变量不用回收)
- java8之后,取消了整个永久代区域,放在元空间中,运行时常量池和静态常量池存放在元空间中,而字符串常量池依然存放在堆中。
常见的字符串常量面试题
package com.mayikt;
/**
* @Description:
* @Author: ChenYi
* @Date: 2020/08/17 14:19
**/
public class Test007 {
public static void main(String[] args) {
String s1 = "a";
String s2 = "b";
String s3 = "a" + "b";
String s4 = s1 + s2;
String s5 = "ab";
//将对象放入字符串常量池中
String s6 = s4.intern();
/**
* 结果为false 因为底层是通过StringBuilder来帮忙拼接的,最后在toString()方法,
* 通过StringBuilder的toString()方法可以看出是new String()对象的,放在堆内存中的,所以两者是不相等的
*/
System.out.println(s3 == s4);
//true
System.out.println(s3 == s5);
//true
System.out.println(s3 == s6);
}
}
总结
- 在字符串常量池中,变量引用方式底层是采用StringBuilder实现的
参考:蚂蚁课堂