最简单的JVM内存结构图
JVM内存结构图
大家好,好几天没有更新了,今天的内容有点多,我们详细介绍下JVM内部结构图,还是和之前一样,案例先行,方便大家理解记忆。
/**
* @author :jiaolian
* @date :Created in 2021-03-10 21:28
* @description:helloworld测试jvm内存区域
* @modified By:
* 公众号:叫练
*/
public class HelloWorldTest { public static void main(String[] args) {
//新建HelloWorldTest对象;
HelloWorldTest helloWorldTest = new HelloWorldTest();
//新建2个线程调用sayHello
for (int i=0; i<2; i++) {
new Thread(()->helloWorldTest.sayHello("world")).start();
}
} /**
* 对某人说hello
* @param who
*/
public void sayHello(String who) {
System.out.println(Thread.currentThread().getName()+"hello!"+who);
}
}
如上代码:在主线程中for循环新建2个线程调用sayHello,最后两个线程分别对世界问好!这段代码比较好理解,就不贴输出结果了。我们编写并运行了这段代码,我们主要看看这段代码在JVM中是怎么运作的。
首先,我们编写一个HelloWorldTest.Java文件,经过javac编译会转化成字节码HelloWorldTest.class,为什么要转化成字节码呢?因为Java虚拟机能识别!最后由类加载子系统ClassLoader将字节码装载到内存。每块内存各有自己的作用,最后由执行引擎来执行字节码。下面我们重点介绍下各块内存发挥的作用!
方法区
方法区主要装一些静态信息,比如:类元数据,常量池,方法信息,类变量等。如上代码HelloWorldTest.class是类元数据,sayHello,main都是方法信息等都是放在方法区存储的。方法区中还需要注意两点:
- 如果方法区太大,超过设置,会报OutOfMemoryError:PermGen space错误。gclib工具可以动态生成类测试该错误。
- 在JDK1.7以前,方法区叫永久代,而1.8之后叫元空间。原因是JDK1.8为了释放管理压力,把运行时常量池交给堆去管理。
堆
堆中主要存放实例对象。你可以这么理解,只要看到用关键字new的对象,数据都放在堆中。如上代码HelloWorldTest helloWorldTest = new HelloWorldTest();helloWorldTest是HelloWorldTest对象的引用,指向new出来的HelloWorldTest对象实例,helloWorldTest引用是放在栈中的,也叫局部变量(方法内申明的对象类型或普通类型),我们简单画图来表示下堆,栈,方法区关系。当JVM执行了HelloWorldTest helloWorldTest = new HelloWorldTest();这句话,JVM内存结构看起来是这样的。如果指向对象引用消失,对象会被GC回收。
在堆内存中,内存需要划分成两块区域,新生代和老年代。如下图所示。
- 新生代:在堆内存中,新生代又分为三块,eden(伊甸园创建新生命,对应new对象),from,to,这三块内存区域都属于新生代,默认比例是8:1:1,每次new对象都会先存储到eden中,如果eden区域内存满了,会触发monitor gc回收该区域,还未回收的对象会放入from或者to,from,to内存其中一块是空的,方便对象在内存中整理标记,每GC一次,from,to两块空间对象每移动一次,还未回收的对象年纪也会增加1,到达一定年纪(默认是15岁),就会进入老年代了。
- 老年代:当老年代满了,会触发Full GC回收,如果系统太大,Full GC都回收不了,程序会出现类似java.lang.OutOfMemoryError: Java heap space,我们可以通过配置JVM参数:如-Xmx32m 设置最大堆内存为32M。
对堆分块原因是方便JVM自动处理垃圾回收。堆内存是GC回收的主要区域。
栈
栈内存空间相对于堆空间比较小,也属于线程私有,栈中主要是一堆栈帧,是先进后出的,理解起来栈帧对应就是一个方法,方法中包含局部变量,方法参数,还有方法出口,访问常量指针,和异常信息表,其中异常信息表和常量指针信息我们在方法体中可能看不出来,但通过工具Jclasslib工具类在反编译class文件可以体现出来,异常信息表可以处理当程序执行报错,会跳转到具体哪行代码执行,JVM中就是通过异常表反馈的。我们还是结合例子和图来详细分析下。当程序运行时,JVM中栈可能如下图呈现状态。
一个线程可能对应多个栈帧,栈帧都是从上往下压入,先进后出,如下图所示,在方法A中调用方法B,在方法B中调用C,在方法C中调用方法D,主线程对应栈帧的压栈情况,出栈顺序是D->C->B->A,最终程序结束。另外还需注意:操作数栈的意思是存储局部变量计算的中间结果,比如在方法A中定义int x = 1;在JVM中会将局部变量入操作数栈用来之后的计算。栈也是有空间大小的,如果栈太大,超过栈深度,会类似报错,java.lang.OutOfMemoryError: Java stack space,最常见的例子就是递归了。你会写demo测试递归例子吗?
程序计数器
程序计数器也是线程独享的,多线程执行程序依赖于CPU分配时间片执行,画个简单的图,看看多线程怎么利用CPU时间片的。如下图,线程0和线程1分配cpu时间片交替执行程序,假设此时线程0先获取到了时间片,时间片用完后CPU会将时间片再分配给线程1,线程1执行完毕后,此时,时间片又回到线程0来执行,那么问题来了,线程0上次执行到哪儿了呢?具体是代码的多少行了呢,该行代码有没有执行完毕?此时程序计数器就发挥作用了,程序计数器保存了线程的执行现场,方便下次恢复运行。这也是为什么程序计数器是线程独享的原因。
本地方法栈
本地方法栈就不过多介绍了,和栈结构一样,是一块独立的区域,只是对应的是native方法。
直接内存
直接内存独立于JVM内存之外的内存,可以直接和NIO接口交互,NIO接口会频繁操作内存,如果放在JVM管理,无疑会增加JVM开销,所以单独将这块提出来,而且直接内存操作数据相比较JVM更快,显而易见提升了程序性能。
内存分配性能优化-逃逸分析
我们之前说过,只要是看到关键字new,对象分配肯定在堆上,下面我们来看一个案例。
/**
* @author :jiaolian
* @date :Created in 2021-03-10 16:10
* @description:逃逸分析测试
* @modified By:
* 公众号:叫练
*/
public class EscapeTest { //private static Object object;
public static void alloc() {
//一个对象相当于16k大小,非逃逸对象
//object = new Object();
Object object = new Object();
} public static void main(String[] args) throws InterruptedException {
//亿次内存
long begin=System.currentTimeMillis();
for (int i=0; i<10000000; i++) {
alloc();
}
long end=System.currentTimeMillis();
System.out.println("time:"+(end-begin));
}
}
如上代码,我们在主函数里面通过for循环1亿次来new Object,一个object为16k,大致估算下有GB数据了,此时我们手动配置JVM参数,-XX:+PrintGC -Xmx10M -XX:+DoEscapeAnalysis;设置打印GC信息,默认最大的堆内存是10M。
- -XX:+PrintGC。表示控制台打印GC信息。
- -Xmx10M。设置最大的堆内存为10M。
- -XX:+DoEscapeAnalysis。 开启逃逸分析(默认开启)。
执行程序,打印结果如下图所示。一共进行了3次GC,你可能有疑问?10M堆内存需要容纳GB数据冲击,怎么也需要N次GC,为什么只有3次GC?如果设置-XX:-DoEscapeAnalysis关闭逃逸分析,GC可能会出现上千次。运行时间也从3毫秒增至1000毫秒以上。说明了非逃逸对象没有新建的堆上,而是建在栈上了。这样做的好处:从程序GC执行次数和执行时间上来看,程序运行效率提高了。
- 原因分析:
观察我们上述案例代码中alloc()方法,方法中Object object = new Object();object是一个局部变量,每次新建后到下一次循环再新建,上一次新建的对象就会出栈,object引用指向的对象就会失效,失效的对象就会被GC回收了。开启逃逸分析后,new Object()创建的对象就不在堆上分配空间了,而放到了栈上。这就是JVM通过逃逸分析对内存的优化。思考下,如果将private static Object object;注释放开,object还会是非逃逸对象吗?
注意:逃逸对象不能在栈上分配空间!
相信到这里你已经对逃逸分析应该有一个比较清晰的认识了。
总结
好了,写的有点累了,写的不全同时还有许多需要修正的地方,希望亲们加以指正和点评,喜欢的请点赞加关注哦。点关注,不迷路,我是【叫练】公众号,微信号【jiaolian123abc】边叫边练。
最简单的JVM内存结构图的更多相关文章
- JVM(三)内存结构图
JVM内存结构图:
- jvm内存模型-回收算法-和内存分配以及jdk、jre、jvm是什么关系(阿里,美团,京东面试题)
1.什么是jvm?(1)jvm是一种用于计算设备的规范,它是一个虚构出来的机器,是通过在实际的计算机上仿真模拟各种功能实现的.(2)jvm包含一套字节码指令集,一组寄存器,一个栈,一个垃圾回收堆和一个 ...
- 简单认识JVM
准备: 在具体聊JVM之前,我们先看两张图,通过分析图,咱们慢慢来聊聊JVM. JVM内存结构图 JVM内存结构脑图 上面两张图中,第二张图相对来说比较直观,就是JVM内存结构都划分成了哪些模块,各个 ...
- jvm内存模型和内存分配
1.什么是jvm? (1)jvm是一种用于计算设备的规范,它是一个虚构出来的机器,是通过在实际的计算机上仿真模拟各种功能实现的. (2)jvm包含一套字节码指令集,一组寄存器,一个栈,一个垃圾回收堆和 ...
- jvm内存模型及分配
1.什么是jvm?(1)jvm是一种用于计算设备的规范,它是一个虚构出来的机器,是通过在实际的计算机上仿真模拟各种功能实现的.(2)jvm包含一套字节码指令集,一组寄存器,一个栈,一个垃圾回收堆和一个 ...
- jvm内存模型-和内存分配以及jdk、jre、jvm是什么关系(阿里,美团,京东)
参考:JVM的垃圾回收机制 总结(垃圾收集.回收算法.垃圾回收器) 1.什么是jvm?(1)jvm是一种用于计算设备的规范,它是一个虚构出来的机器,是通过在实际的计算机上仿真模拟各种功能实现的.(2) ...
- JVM内存简单理解
1.首先简单说一下CPU与内存之间的关系 CPU运转速度快,磁盘的读写速度远远不及CPU运转速度,所以设计了内存来缓冲CPU等待磁盘读写:随着CPU的发展,内存读写也远远跟不上CPU的读写速度,CPU ...
- 最简单例子图解JVM内存分配和回收
一.简介 JVM采用分代垃圾回收.在JVM的内存空间中把堆空间分为年老代和年轻代.将大量(据说是90%以上)创建了没多久就会消亡的对象存储在年轻代,而年老代中存放生命周期长久的实例对象.年轻代中又被分 ...
- 你需要简单了解JVM中的内存长什么样子
下面有关JVM内存,说法错误的是? 1.程序计数器是一个比较小的内存区域,用于指示当前线程所执行的字节码执行到了第几行,是线程隔离的 2.Java方法执行内存模型,用于存储局部变量,操作数栈,动态链接 ...
随机推荐
- C++ part4
红黑树 references: 红黑树详细分析,看了都说好 关于红黑树(R-B tree)原理,看这篇如何 性质: 1.节点是红色或黑色 2.根节点是黑色 3.叶子节点(叶子节点均为NULL)都是黑色 ...
- 我是sakebow:新人报到,请多关照!
大家好 这里是sakebow,实际上是从CSDN转生过来的(说得好像在CSDN死了一样),在那边是ordinary_brony.我的GitHub名字也是sakebow 来这里干什么 主要还是想试试做个 ...
- reStructuredText(.rst) && read the docs
Read the Docs && reStructuredText (.rst) && markdown 1. github master 分支,创建 docs ...
- js type automatic conversion
js type automatic conversion String & Number `255` < 16; false `15` < 16; true `25` < 1 ...
- js & touch & swiper
js & touch & swiper https://developer.mozilla.org/en/docs/Web/API/Touch_events "use str ...
- 10月上线的NGK global有怎样的发展前景?
随着NGK global 10月份的上线时间将近,社区中也开始纷纷讨论.预测起NGK global上线后的表现,对此小编也有一些自己的理解,就此分享给大家. 在基于实体生态的赋能下,NGK globa ...
- 主打开放式金融的Baccarat项目如何开疆拓土?
DeFi在这个夏天迎来了大爆发,像无托管交易.流动性挖矿.保险协议.NFT代币都在今年看到了有别于以往的应用.随着比特币走入主流,DeFi热度下降,不少人都觉得DeFi热潮已死.但事实是,DeFi的总 ...
- 在next主题添加微信公众号二维码
在侧边栏添加微信公众号二维码 首先,当然是准备一张微信公众号二维码.有两种添加方式,添加到侧边栏或者添加到推文的结尾处.我的next主题是7.x版本的,使用的主题是Gemini,设置的侧栏显示方式是一 ...
- Maven报错:Unsupported major.minor version 51.0
这个错误时因为JDK版本的问题,比如本机的JDK为1.6,但是项目编译时用的JDK为1.7那么就会出现这个异常,因为本机JDK版本较低不能执行编译版本为高版本的Class文件,各JDK版本对应的错误编 ...
- IntelliJ Idea tomcat 控制台输出乱码
reference: https://blog.csdn.net/dandandeshangni/article/details/485442211. 在运行/调试 配置对话框的Startup/Con ...