面试官问我平时写的Bug的存储位置(逃逸分析、标量替换、锁消除)

本文探讨了Java程序中Bug的存储位置,重点讲解了JVM内存结构,包括堆、虚拟机栈、本地方法栈、方法区和程序计数器。通过逃逸分析和标量替换,了解对象如何在堆和栈上分配,以及如何通过锁消除优化性能。同时,介绍了堆中的TLAB技术及其问题,以及线程栈的局部变量表、操作数栈等功能。最后,文章讨论了方法区和程序计数器的作用,帮助理解Java内存管理的细节。
摘要由CSDN通过智能技术生成

在这里插入图片描述

前言

爱生活,爱技术,更爱左耳君,么么哒😘

说到写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编译器有两个优化,叫做逃逸分析和标量替换;

逃逸分析,听着有点意思&

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值