布朗大学(美国)校训:“我们信赖上帝。”
转眼已进入深秋,在书房里听听歌写写文章,蛮舒服惬意的事情!
一 Java堆(Heap)
这个大家估计耳熟能详,随口便说是放置new对象的地方,没错,主要是存储对象实例,几乎所有的对象实例都在这个区域分配内存。看到这里估计有朋友问了,那是不是有的对象实例不是在堆上分配的,jvm还真提供了,是否听说过栈上分配和TLAB,栈上分配是为了减少gc的压力,因为随栈弹出而释放并不会出发gc,TLAB则是优化对象实例在堆中并发分配的效率,具体详细可自行百度了解,这里放一张对象分配图,摘自实战Java虚拟机一书中:堆是jvm管理内存最大的一块,是线程共享的一块区域,在jvm启动时创建;可能有些同学经常遇到OOM(OutOfMemoryError)异常,这里考大家一题,如何模拟OOM?
很简单,首先设置下jvm参数(-Xms 1m -Xmx 2m),然后在main函数初始化一个大的数组即可浮现。
二 方法区(Method Area)
与堆一样,也是被线程共享的区域,同样会出现OOM(OutOfMemoryError)异常,此区域主要存储如下信息:
1/已被虚拟机加载的类信息,即元数据;
2/静态变量和常量,这里维护一个运行时常量池;
3/JIT(即时编译)后的代码,这个上节有提到过;
这里,分享一个常考的面试题:
/**
* @author 阿伦故事
* @Description:
* 小试牛刀
* */
public static void main(String[] args) {
String s = "1"+"2"+"3";// 1 这里创建了几个对象?
String s1 = "allen story";// 2
String s2 = new String("allen story");// 3 这里创建了几个对象?
}
/**
* 分析如下:
* 1处只创建了一个对象,即“123”在运行时常量池,栈中引用s直接指向常量池
* 3处也只创建一个对象,即在堆中new了一个对象实例,“allen story”在2处已
* 经在运行时创建,堆中对象直接存储该指向即可。
* */
三 程序计数器(Program Counter Register)
了解jvm内存结构规范的朋友们,肯定脱口而出,pc用于记录所属线程所执行的字节码的执行行号,没错,有多少朋友理解这个意思?如果了解寄存器的话,那就容易很多,cpu执行的指令就是从指令寄存器取的,而pc就是用于存储下一条指令的偏移地址。当然,pc是线程私有的,存储的自然所属线程的指令地址。
唯一一个在JVM规范中没有规定任何OutOfMemoryError的区域。
如何理解?
很简单,pc只存储下一条指令的地址,并不会随着指令量级的增长而增长。
四 栈(Stack)
栈分为本地方法栈和虚拟机栈,顾名思义,一个是用于native方法的,栈的特性是先进后出,这也是递归调用的逻辑。栈是线程私有的,每启动一个新线程时,jvm都会为它分配一个Java栈,以栈帧为单位保存线程的运行状态。
虚拟机只会对Java栈执行两种操作:以栈帧为单位的入栈或者出栈。
这里也容易出现一个异常,StackOverflowError,如何模拟?
理解了就很容易,超过了栈深度不就异常了嘛?
只需要写个递归调用,无返回不退出,一直调用无退出条件,那不就一直在入栈而不弹栈,那不栈溢出了嘛?另可通过测试得知jvm为每个线程分配的内存默认是1M,具体可测试下,即jvm参数(-Xss 1m)。
栈在jvm内存结构中非常重要,我们有必要详细了解它的组成部分,由基本类型变量区、执行环境上下文、操作指令区(存放操作指令)组成,只是保存基本类型数据和自定义对象的引用,即堆中实例对象的地址或偏移地址。
五 小结
介绍了jvm内存结构划分,稍稍总结下重点区域,堆/方法区/栈之间的关系(如图),以一个例子串下类的加载/分配与执行。
Sample:
/**
* @author 阿伦故事
* @Description:
* jvm执行流程
* */
public static void main(String[] args) {
Person person = new Person();
person.sayHello();
}
/**
* 流程如下:
* 上述很简单,就是main主线程创建一个person对象实例,调用其sayHello方法
* 1/先去方法区寻找Person类的元数据信息;
* 2/如果找不到,Classloader加载Person类信息存储内存方法区;
* 3/在堆中创建Person对象,并持有方法区中Person的元信息的引用;
* 4/把引用变量person添加到栈中,指向堆中的内存对象Person实例;
* 5/执行person.sayHello()时,JVM根据person定位到堆空间的Person实例;
* 6/根据Person实例持有的方法区引用,获取Person元信息与sayHello方法执行字节码。
* */
特此声明:
分享文章有完整的知识架构图,将从以下几个方面系统展开:
1 基础(Linux/Spring boot/并发)
2 性能调优(jvm/tomcat/mysql)
3 高并发分布式
4 微服务体系
如果您觉得文章不错,请关注阿伦故事,您的支持是我坚持的莫大动力,在此受小弟一拜!