JVM基础及JVM内存结构

程序计数器


作用:记录下一次要执行的JVM指令的内存地址

运作:当一个线程启动时,JVM就会为该线程创建一个私有的程序计数器内存,线程运行中执行Java代码,在JVM底层来看都是执行其对应的Java字节码中的JVM指令,而每个JVM指令都有一个行号(即该指令在内存中的地址),线程将要执行的下一条指令内存地址存入其私有的程序计数器中,当线程抢夺到CPU时间片时,JVM执行引擎中的解释器就会去该线程的程序计数器中获取要解释的指令,并解释为操作系统级别的机器码,交给CPU执行。当线程失去CPU时间片时,线程会继续将下一个要执行的指令的地址存入其私有的程序计数器,等到再次获取CPU时间片后,可以接着执行。

特点

  1. 线程私有(随着线程的启动而创建,随着线程的死亡而销毁)
  1. 不存在内存溢出(只存储下一条执行指令的地址值)
  1. 内存小,速度快,物理实现是寄存器

注意:对于非本地方法,即Java方法,程序计数器记录的是下一条将要执行的JVM指令的地址,对于本地方法,即Native方法,程序计数器记录的是空。

虚拟机栈


虚拟机栈的组成单位是:栈帧。

栈帧的组成是:局部变量表,操作数栈,方法返回地址,动态链接。

虚拟机栈:JVM会每个线程创建一个虚拟机栈内存。即虚拟机栈是线程运行过程中需要的内存。虚拟机栈是线程私有的。

栈帧:线程中每次方法调用对应虚拟机栈有一个栈帧入栈,每次方法调用结束都对应虚拟机栈一个栈帧出栈。即栈帧是方法运行过程中需要的内存。

注意:线程只能有一个活动栈帧,就是当前正在执行的方法,即虚拟机栈顶的栈帧。

局部变量表:方法运行中方法参数,局部变量所需要的内存,存储单位称为槽位。

操作数栈:方法运行中方法参数,局部变量运算所需要的内存。

返回地址:方法出口。

动态链接:是找到正确的方法入口,然后才有后来的进栈出栈的执行。

问题0:虚拟机栈是否会出现内存溢出?

会,虚拟机栈是一种栈结构,它有着入栈出栈两种行为。当入栈的栈帧数目大于虚拟机栈的深度时,就会导致java.lang.StackOverFlowError。现实例子比如方法的递归调用,但是递归方向是未知的。还有一种情况是单个栈帧内存过大,直接超出了虚拟机栈剩余内存大小,比如一个方法中定义了过多的变量,此时会报错java.lang.StackOverFlowError。

还有一种情况,就是创建过多的线程,导致没有足够的内存给新线程创建虚拟机栈,此时会报错java.lang.OutOfMemoryError。

问题1:虚拟机栈内存是否越大越好?

JVM可以通过-Xss参数来设置单个线程的虚拟机栈的内存大小,如果单个线程的栈内存设置的越大,则总线程数越少。另外当创建的线程过多时,可能出现OOM。

问题2:虚拟机栈内存是否需要被垃圾回收?

不需要,因为虚拟机栈的生命周期就是线程的生命周期。当线程结束时,虚拟机栈内存被JVM自动回收。相较于堆中对象,虚拟机栈中的数据生命周期更短且更容易判断是否存活。

问题3:方法内的局部变量是否线程安全?

线程安全场景出现的前提是:1.多线程环境 2.多线程共享操作同一个数据

一个方法被多线程执行,

如果方法内的局部变量来自于外部传入(即方法参数),则可能该局部变量线程不安全,

如果方法内的局部变量会作为方法返回值返回,则可能局部变量线程不安全,

如果方法内的局部变量,既不从外部传入,也不传出给外部,则局部变量线程安全。

当然前面三个判断是以局部变量是引用类型为前提的,如果方法内局部变量是基本类型,则一定线程安全。

本地方法栈


和虚拟机栈没有本质区别。

只是虚拟机栈是给Java方法用的,本地方法栈是给Native方法用的。

本地方法栈也会出现StackOverFlowError和OOM。

定义:所有的对象实例和数组实例都存在堆中。

特点

  1. 线程共享,堆中对象都需要考虑线程安全的问题
  1. 堆内存是GC管理的内存区域,可以实现垃圾对象内存回收。
  1. 堆内存是实现分代的,如经典分代将堆分为两块:新生代,老年代
  1. 堆内存可以划分出线程私有的TLAB,即本地线程分配缓冲区,提高内存分配效率,减少内存分配冲突。
  1. 堆内存在物理上可以是不连续的内存组成,但是逻辑上是连续的。
  1. 堆内存既可以是固定大小的,也可以可扩展大小的。可以通过虚拟机参数控制:
  • -Xms 堆初始内存大小
  • -Xmx 堆最大内存大小

注意:当堆内存不足以分配时,就会发生堆内存溢出,即报错java.lang.OutOfMemoryError:Java heap space

方法区


定义:是线程共享的内存区域。方法区是堆的逻辑部分,但是也叫非堆。

作用:存储加载的类型信息,常量,静态变量,即时编译器编译代码的缓存

实现:方法区只是一种概念,对于Hotspot虚拟机,它的具体实现分为两阶段

  1. JDK8:元空间,元空间处于本地内存中
  1. JDK8以前:永久代,永久代处于JVM内存中

设置方法区内存大小的虚拟机参数变化

JDK8:-XX:MaxMetaspaceSize=

JDK8以前:-XX:MaxPermSize=

方法区内存溢出报错变化

JDK8:java.lang.OutOfMemoryError:Metaspace

JDK8以前:java.lang.OutOfMemoryError:PermGen space

问题:是否会存在方法区溢出的场景?

会,大量加载类到方法区,当前流行的Spring,Mybatis框架都使用了cjLib,即底层使用ClassWriter原理自动生成二进制类。

运行时常量池


常量池和运行时常量池的关系:

常量池是class文件,即二进制字节码文件中的Constant Pool信息,当字节码被类加载器加载到方法区后,它的Constant Pool信息就会放入运行时常量池,并把里面的符号地址变为真实地址。

串池和运行时常量池的关系:

运行时常量池中的字符串仅仅是符号,而不是对象。只有对应字符串符号第一次被使用时,才会变成字符串对象,并存入串池。

串池特点:

  1. 字符串对象的延迟加载特性:运行时常量池中的字符串仅仅是符号,只有运行时常量池中的字符串符号第一次被使用时才会变成字符串对象。
  1. 字符串对象的去重特性:当需要使用字符串时,会先使用字符串符号去串池中查找是否由对应的字符串对象,如果有,则直接引用串池中的字符串对象。
  1. 可以使用String::intern方法,主动将串池中还没有的字符串对象放入串池
  1. 字节串+号拼接优化:

对于字符串变量+拼接:底层new StringBuilder().append(“x”).append(“y”).toString();

对于字符串常量+拼接:编译期优化,则直接替换为拼接结果字符串对象。

串池位置:

JDK6:永久代

JDK7:堆

问题:如何验证串池位置

我们可以利用String::intern()方法往串池中加入大量字符串对象,撑爆内存,使之抛出OOM,对于永久代内存溢出报错:java.lang.OutOfMemoryError:PermGen space,对于堆内存溢出报错:java.lang.OutOfMemoryError:Java heap space,所以可以通过内存溢出报错来辨别串池位置。

问题:为什么要将串池位置从永久代换到堆中?

因为Java程序运行中会产生大量的字符串对象,如果不及时回收字符串对象内存,很有可能造成内存溢出,而永久代只有在Full GC时才会进行回收,而堆内存新生代会经常触发Minor GC,代价更小,频率更高。

问题:串池位置的变化对String::intern()方法返回结果的影响?

JDK6时,串池在永久代中,堆中字符串对象调用intern方法后,会尝试去串池中查找是否已存在对应字符串对象,若不存在,则复制堆中字符串对象到串池中,此时串池中复制对象和堆中原对象不是同一个对象。

最后

中年危机是真实存在的,即便有技术傍身,还是难免对自己的生存能力产生质疑和焦虑,这些年职业发展,一直在寻求消除焦虑的依靠。

  • 技术要深入到什么程度?

  • 做久了技术总要转型管理?

  • 我能做什么,我想做什么?

  • 一技之长,就是深耕你的专业技能,你的专业技术。(重点)

  • 独立做事,当你的一技之长达到一定深度的时候,需要开始思考如何独立做事。(创业)

  • 拥有事业,选择一份使命,带领团队实现它。(创业)

一技之长分五个层次

  • 栈内技术 - 是指你的前端专业领域技术

  • 栈外技术 - 是指栈内技术的上下游,领域外的相关专业知识

  • 工程经验 - 是建设专业技术体系的“解决方案”

  • 带人做事 - 是对团队协作能力的要求

  • 业界发声 - 工作经验总结对外分享,与他人交流

永远不要放弃一技之长,它值得你长期信仰持有

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

主要内容包括html,css,html5,css3,JavaScript,正则表达式,函数,BOM,DOM,jQuery,AJAX,vue 等等。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值