前言
爱生活,爱技术,更爱左耳君,么么哒😘
说到写bug,我们每天都在用Java实现着各种需求,我们实现的Java程序每天都运行在每个机器的虚拟机上,但是你了解你写的代码的具体存储位置吗
说实话,这个东西,在我刚开始学Java的时候,我听到JVM虚拟机这个名词的时候,我的感觉是这个样子的(惭愧
你们肯定也会有些疑问吧,平时写的代码每一部分都是存储在哪里的?
是的,没错,我的内心就像拖着下巴的那位,除了,模样,emmm…
虽然现在也不是多么的精通,但是比之前好太多了,不是涉及很底层的东西也算是了解一些,当然真要是问我各种涉及细节,毫不谦虚的说,以我的水平,我可能只会阿巴阿巴(逃
如果大家对更深入的JVM感兴趣,可以和JVM大神R大这种多去沟通沟通
是的,没错,其实我这个文章算是扫盲文章,但是在扫盲文章的基础上说的更细一点,更多一点,我也会给大家抛出一些面试官爱问的问题,并且帮大家解答,所以大家请尽情读下去,肯定会让你有所收获
大家觉得不错的点个关注,大家一起探讨、一起学习、一起进步
JVM内存结构
JVM内存布局,先给大家上个图
如果你是读过JVM文章的养鱼仔的话,那你肯定看过上面类似的图,我在给大家放一张,大家在熟悉一遍,看过的回一下,没看过的混个脸熟
JVM内存主要分为堆、虚拟机栈、本地方法栈、方法区、程序计数器等
堆是虚拟机内存占据最大的一部分,堆的目的就是盛放大量的对象实例的;虚拟机栈对应的是方法的执行过程,本地方法栈是用来调用本地方法的执行过程;方法区就是用来存储存储类信息、常量、静态变量的数据,是线程共享的数据;程序计数器,就是存储着线程下一条将要执行的指令
每个区域都有其特定的功能,就像是一个企业,一个工作室,每个人发挥着自己的长处,各司其职
虚拟机堆
Java堆是垃圾收集器管理的主要地方,因此很多的时候也被称为GC堆,Java堆还可以分为年轻代和老年代,年轻代又可以分为Eden空间、From Survivor空间、To Survivor空间,默认是8:1:1的比例
根据Java虚拟机规范的规定,Java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可,就像我们的磁盘空间一样
在实现时,既可以实现成固定大小的,也可以是可扩展的,不过当前主流的虚拟机都是按照可扩展来实现的(通过-Xmx和-Xms控制);
如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。
打断一下,Java堆的区域都是线程共享的吗?
当你听到这个问题的时候,你首先想到的是什么呢?
let me tell you,面试官其实问这个的时候就是在看你对堆的了解程度,你只知道是用来放对象实例的,那面试官对你表现觉得不算非常满意;
但是如果你知道TLAB,并且知道它的原理和问题,那面试官就会觉得:这小伙子不一般,我得再多深入了解了解,可以考虑当我的好助手
首先,你得肯定回答,没错,堆是全局共享的,但是会存在一些问题,那就是多个线程在堆上同时申请空间,如果在并发的场景中,两个线程先后把对象引用指向了同一个内存区域,那可能就会出现问题;
为了解决这个问题呢,就得进行同步控制,说到同步控制,就会影响到效率
就拿Hotspot来举例子,它的解决方案是每个线程在堆中都预先分配一小块内存,然后再给对象分配内存的时候,先在这块“私有内存”进行分配,这块用完之后再去分配新的“私有内存”,这就是TLAB分配
你也看到了,我加引号了,它并不是真正意义上的私有,而是表面上的私有;它是从堆内存划分出来的,有了TLAB技术,堆内存并不是完完全全的线程共享,每个线程在初始化的时候都会去内存中申请一块TLAB
切记:并不是TLAB区域的内存其它线程完全无法访问,其它线程也是可以读取的,只不过无法在这个区域分配内存而已
说到这的时候,也给面试官一个眼神,说明我的干货还没完,我还能继续吹
难道TLAB很完美吗?所谓,金无足赤人无完人,肯定有他的问题所在
我们知道TLAB是线程特有的,它的内存区域不是很大,所以会出现一些不够用的情况,比如一个线程的TLAB的空间有100KB,其中已经使用了80KB,如果还需要再分配一个30KB的对象,则无法直接在TLAB上分配了,这种情况有两种解决办法
直接在堆中分配
废弃当前TLAB,重新申请TLAB空间再次进行内存分配
其实两种各有利弊,第一种的缺点就是存在一种极端情况,TLAB只剩下1KB,就会导致后续的分配可能大多数对象都需要直接在堆中分配;第二种的就是可能会出现频繁的废弃TLAB、频繁申请TLAB的情况
为了解决这两个方案存在的问题,虚拟机定义了一个refill_waste的值,这个值可以翻译为“最大浪费空间”。
当请求分配的内存大于refill_waste的时候,会选择在堆内存中分配。若小于refill_waste值,则会废弃当前TLAB,重新创建TLAB进行对象内存分配
那你刚刚说的,几乎所有对象实例都存储在这里,是还有例外吗?能详细解释下吗?
是的,亲爱的面试官,Java对象实例和数组元素不一定都是在堆上分配内存,满足特定的条件的时候,它们可以在栈上分配内存
面试官微微一笑,什么情况呢?
亲爱的面试官,是这样子的,JVM中的Java JIT编译器有两个优化,叫做逃逸分析和标量替换;
逃逸分析,听着有点意思&