一、java内存区域:
1、程序计数器(线程私有):
内存中较小的内存空间,可以当做当前线程所执行字节码的行号指示器。如分支、循环、跳转、异常处理、线程恢复都需要依赖这个计数器完成。
2、java虚拟机栈(线程私有):
也就是我们通常所说的“堆栈”中的栈。栈是由一个个栈帧组成的。栈帧中存放局部变量表、各种基本的数据类型(boolean、byte、char、short、int、float、long、double)、对象引用、方法返回地址等。当线程执行一个方法时,就会随之创建一个对应的栈帧,并将建立的栈帧压栈。当方法执行完毕之后,便会将栈帧出栈。所以递归调用容易出现StackOverflowError异常,即线程请求的栈深度超过了虚拟机允许的最大深度。
每个线程分配的栈的大小是设置的(-Xss来设置),栈的基本单位是栈帧。所以栈帧所占空间越大,栈的深度就越小,反之亦然。简单的可以理解为:栈的大小=栈帧大小平均值*栈的深度。
3、本地方法栈(线程私有):
与虚拟机栈发挥的作用类似,只不过本地方法栈使用的本地方法服务(Native方法服务)。
4、java堆(所有线程共享):
在虚拟机启动时创建,存放对象的实例。
从GC回收的角度来说,可细分为新生代、老年代。
更细致的把新生代拆分为Eden空间、From Survivor空间、To Survivor空间等。
可以通过-Xms和-Xmx控制分配的堆的大小。当堆内没有足够的内存完成实例分配,且到达堆的大小上限,会抛出OutOfMemoryError异常,即我们经常说的OOM。
5、方法区(所有线程共享):
存放已被虚拟机加载的类的信息、常量池、静态变量、即时编译器编译后的代码(JIT)、64位系统上类压缩指针等数据。
逻辑上虽为java虚拟机堆的一部分,为了和java堆区分开来,又称为Non-Heap(非堆)。
在jdk1.8之前方法区通过“永久代”(PermGen)来实现,在jdk1.8及以后废弃,采用“元空间”(MetaSpace)来实现。最大的区别是永久代是在jvm虚拟机上分配的内存,而元空间是分配的本地内存。可通过-XX:MetaspaceSize和-XX:MaxMetaspaceSize配置元空间分配的大小和上限。
更细致的拆分MetaSpace(元空间)、CodeCache(代码缓存区,主要存放JIT编译生成的二进制代码)、CompressedClassPointSpace(压缩类指针空间,主要用于64位系统上类压缩指针)。
二、总结:
整理下,输出个思维导图。
参考资料:
《深入理解JAVA虚拟机》