JVM类加载与运行时数据区

目录

一、类加载器

jvm类的加载过程

第一阶段:加载

第二阶段:链接阶段

第三阶段:初始化阶段:

双亲委派机制

沙箱安全机制

运行时数据区

栈-Xss1m

TLAB

逃逸分析

方法区

常量池中有什么

StringTable为什么要调整位置

静态变量存放在那里?

对象实例化内存布局与访问定位

运行时元数据

类型指针

实例数据(Instance Data)

对齐填充说明

StringTable

注意

字符串拼接操作


一、类加载器

jvm类的加载过程

第一阶段:加载

通过一个类的全限定名获取定义此类的二进制字节流

将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构

在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口

第二阶段:链接阶段

验证 :文件格式 元数据验证 字节码验证 符号引用验证

准备:为类变量分配内存 设置默认值

解析: 符号引用转换为直接引用

事实上,解析操作往往会伴随着JVM在执行完初始化之后再执行。解析动作主要针对类或接口、字段、类方法、接口方法、方法类型等。对应常量池中的CONSTANT Class info、CONSTANT Fieldref info、CONSTANT Methodref info等

第三阶段:初始化阶段:

为静态变量赋予正确的初始值

执行类构造器 虚拟机必须保证一个类的<clinit>()方法在多线程下被同步加锁。 从上面可以看出初始化后,只能够执行一次初始化,这也就是同步加锁的过程

双亲委派机制

方法 getParent()

loadClass() 双委派机制

findClass() 编写加载规则 将取得的字节码转换成流

defineClass() 生成类的class对象

  protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{synchronized (getClassLoadingLock(name)) {// 首先,检查类是否已经加载Class<?> c = findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();try {if (parent != null) {//类加载器有父类加载器,使用父类加载器加载c = parent.loadClass(name, false);} else {//直到顶级类加载器,即parent为空时,由findBootstrapClassOrNull()方法尝试到Bootstrap ClassLoader中检查目标类。c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found// from the non-null parent class loader}if (c == null) {// 如果仍未找到,则调用findClass来查找该类。long t1 = System.nanoTime();//通过findClass()方法尝试到对应的类目录下去加载目标类。c = findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {//如果加载成功,则根据resolve参数决定是否要执行连接过程,并返回Class对象。resolveClass(c);}return c;}}

Service Provider Interface,SPI)的代码,现在问题来了,启动类加载器是绝不可能认识、加载这些代码的,那该怎么办?(SPI:在Java平台中,通常把核心类rt.jar中提供外部服务、可由应用层自行实现的接口称为SPI)

为了解决这个困境,Java的设计团队只好引入了一个不太优雅的设计:线程上下文类加载器(Thread Context ClassLoaden),这个类加载器可以通过java.lang.Thread类的setContextClassLoader()方法进行设置,如果创建线程时还未设置,它将会从父线程中继承一个,如果在应用程序的全局范围内都没有设置过的话,那这个类加载器默认就是应用程序类加载器。

JNDI服务使用这个线程上下文类加载器去加载所需的SPI服务代码,这是一种父类加载器去请求子类加载器完成类加载的行为,这种行为实际上是打通了双亲委派模型的层次结构来逆向使用类加载器,已经违背了双亲委派模型的一般性原则。在JDK 6时,DK提供了java.util.ServiceLoader类,以META-INF/services中的配置信息,辅以责任链模式,这才算是给SPI的加载提供了一种相对合理的解决方案。

Java虚拟机对class文件采用的是按需加载的方式

  • 避免类的重复加载

  • 保护程序安全,防止核心API被随意篡改

沙箱安全机制

沙箱机制就是将Java代码限定在虚拟机(VN)特定的运行范围中,并且严格限刺代码对本地系统资源访问。通过这样的措施来保证对代码的有限隔离,防止对本地系统造成破坏。

运行时数据区

栈-Xss1m

  • 局部变量表(Local Variables)

  • 操作数栈(operand Stack)(或表达式栈)

  • 动态链接(DynamicLinking)(或指向运行时常量池的方法引用)

  • 方法返回地址(Return Address)(或方法正常退出或者异常退出的定义)

每一个栈帧内部都包含一个指向运行时常量池该栈帧所属方法的引用。包含这个引用的目的就是为了支持当前方法的代码能够实现动态链接(Dynamic Linking)。比如:invokedynamic指令

比如:描述一个方法调用了另外的其他方法时,就是通过常量池中指向方法的符号引用来表示的,那么动态链接的作用就是为了将这些符号引用转换为调用方法的直接引用。

堆可以处于物理上不连续的内存空间中,但在逻辑上它应该被视为连续的。

所有的对象实例以及数组都应当在运行时分配在堆上。

  • “-Xms"用于表示堆区的起始内存,等价于-xx:InitialHeapSize

  • “-Xmx"则用于表示堆区的最大内存,等价于-XX:MaxHeapSize

通常会将-Xms和-Xmx两个参数配置相同的值,其目的是为了能够在ava垃圾回收机制清理完堆区后不需要重新分隔计算堆区的大小,从而提高性能

  • Eden:From:to -> 8:1:1

  • 新生代:老年代 - > 1 : 2

几乎所有的Java对象都是在Eden区被new出来的。绝大部分的Java对象的销毁都在新生代进行了。(有些大的对象在Eden区无法存储时候,将直接进入老年代)

YGC次数达到15次还存活的对象,会移动到老年代。次数可以设置。默认是15次。

  • 针对幸存者s0,s1区的总结:复制之后有交换,谁空谁是to

  • 关于垃圾回收:频繁在新生区收集,很少在老年代收集,几乎不再永久代和元空间进行收集

  • 新生代采用复制算法的目的:是为了减少内碎片

部分收集:不是完整收集整个Java堆的垃圾收集。其中又分为:

  • 新生代收集(MinorGC/YoungGC):只是新生代(Eden\s0,s1)的垃圾收集

  • 老年代收集(MajorGC/o1dGC):只是老年代的圾收集。

TLAB

VM为每个线程分配了一个私有缓存区域,它包含在Eden空间内。

多线程同时分配内存时,使用TLAB可以避免一系列的非线程安全问题,同时还能够提升内存分配的吞吐量,因此我们可以将这种内存分配方式称之为快速分配策略

逃逸分析

随着JIT编译期的发展与逃逸分析技术逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化,所有的对象都分配到堆上也渐渐变得不那么“绝对”了。

  • 栈上分配:将堆分配转化为栈分配。如果一个对象在子程序中被分配,要使指向该对象的指针永远不会发生逃逸,对象可能是栈上分配的候选,而不是堆上分配

  • 同步省略:如果一个对象被发现只有一个线程被访问到,那么对于这个对象的操作可以不考虑同步。

  • 分离对象或标量替换:有的对象可能不需要作为一个连续的内存结构存在也可以被访问到,那么对象的部分(或全部)可以不存储在内存,而是存储在CPU寄存器中。

    就会把这个对象拆解成若干个其中包含的若干个成员变量来代替.这个过程就是标量替换

方法区

“尽管所有的方法区在逻辑上是属于堆的一部分,但一些简单的实现可能不会选择去进行垃圾收集或者进行压缩

方法区主要存放的是 Class,而堆中主要存放的是 实例化的对象

  • 加载大量的第三方的jar包

  • Tomcat部署的工程过多(30~50个)

  • 大量动态的生成反射类

常量池中有什么
  • 数量值

  • 字符串值

  • 类引用

  • 字段引用

  • 方法引用

运行时常量池(Runtime Constant Pool)是方法区的一部分。

常量池表(Constant Pool Table)是Class文件的一部分,用于存放编译期生成的各种字面量与符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中

运行时常量池,相对于Class文件常量池的另一重要特征是:具备动态性

StringTable为什么要调整位置

stringTable回收效率不高。而我们开发中会有大量的字符串被创建,回收效率低,导致永久代内存不足。放到堆里,能及时回收内存。

静态变量存放在那里?

静态引用对应的对象实体始终都存在堆空间

方法区的垃圾收集主要回收两部分内容:常量池中废弃的常量和不再使用的类型。

只要常量池中的常量没有被任何地方引用,就可以被回收

对象实例化内存布局与访问定位

对象的实例化

对象头包含了两部分,分别是 运行时元数据(Mark Word)和 类型指针

运行时元数据
  • 哈希值(HashCode)

  • GC分代年龄

  • 锁状态标志

  • 线程持有的锁

  • 偏向线程ID

  • 偏向时间戳

类型指针

指向类元数据InstanceKlass,确定该对象所属的类型。指向的其实是方法区中存放的类元信息

实例数据(Instance Data)

对齐填充说明

不是必须的,也没有特别的含义,仅仅起到占位符的作用

StringTable

注意

字符串常量池是不会存储相同内容的字符串的

String的string Pool是一个固定大小的Hashtable,默认值大小长度是1009。如果放进string Pool的string非常多,就会造成Hash冲突严重,从而导致链表会很长,而链表长了后直接会造成的影响就是当调用string.intern时性能会大幅下降。

使用-XX:StringTablesize可设置stringTab1e的长度

字符串拼接操作

  • 常量与常量的拼接结果在常量池,原理是编译期优化

  • 常量池中不会存在相同内容的变量

  • 只要其中有一个是变量,结果就在堆中。变量拼接的原理是StringBuilder

  • 如果拼接的结果调用intern()方法,则主动将常量池中还没有的字符串对象放入池中,并返回此对象地址

从上述的结果我们可以知道:

如果拼接符号的前后出现了变量,则相当于在堆空间中new String(),具体的内容为拼接的结果

而调用intern方法,则会判断字符串常量池中是否存在JavaEEhadoop值,如果存在则返回常量池中的值,否者就在常量池中创建

s1 + s2的执行细节

  • StringBuilder s = new StringBuilder();

  • s.append("a");

  • s.append("b");

  • s.toString(); -> 类似于new String("ab");

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/235940.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何制作一个数字人?流程是怎样的?

​​虚拟人概念从早期动漫领域的初音未来用一首《甩葱歌》让绿色双马尾的“歌姬”形象火出圈&#xff0c;到洛天依登上央视春晚舞台&#xff0c;再到众多虚拟歌手、虚拟主播、KOL、代言人等活跃于各种情境中。 去年以来&#xff0c;元宇宙和虚拟人技术进入全球科技产业的聚光灯…

鸿蒙HarmonyOS应用开发-ColumnRow组件

1 概述 一个丰富的页面需要很多组件组成&#xff0c;那么&#xff0c;我们如何才能让这些组件有条不紊地在页面上布局呢&#xff1f;这就需要借助容器组件来实现。 容器组件是一种比较特殊的组件&#xff0c;它可以包含其他的组件&#xff0c;而且按照一定的规律布局&#xf…

java第二十六课

数据库多表 多表做到每个表的字段名称不一样 Mysql 关系数据库 结合到商城&#xff1a;用户表 订单表 商品表 商品详情表 用户表:字段&#xff1a; 用户 id:唯一标志用户 用户名称&#xff1a;name 用户性别&#xff1a;sex 用户年龄:age 用户地址&#xff1a;position 用户密码…

wyler水平仪维修WYLER倾角仪维修CH-8405

瑞士WYLER电子水平仪维修&#xff1b;BIueCLINO倾斜度测量仪维修&#xff1b;wyler电子倾角仪维修。 水平仪常见故障及处理方法 1、 仪表通电不工作。 A、检查仪表220V电源端子接线是否正确 B、检查仪表电容是否熔断&#xff1b; C、拧下仪表后的固定螺钉&#xff0c;将表…

五、Linux基础软件安装

目录 1、安装JDK 2、安装Mysql 接下来&#xff0c;我们来学习如何在linux上安装JDK和Mysql&#xff0c;安装之前需要在Linux上提前创建三个目录: mkdir -p /export/software #软件包存放目录 mkdir -p /export/server #安装目录 mkdir -p /export/data #数据存放目录…

机器人AGV小车避障传感器测距

一、A22超声波传感器 该模块是基于机器人自动控制应用而设计的超声波避障传感器&#xff0c;针对目前市场上对于超声波传感器模组盲区大、测量角度大、响应时间长、安装适配性差等问题而着重设计。 具备了盲区小、测量角度小、响应时间短、过滤同频干扰、体积小、安装适配性高…

10-微信小程序 图片 相机 二维码 动画相关API(实现选择相册、拍照、录像、动画)

10-微信小程序 图片 相机 二维码 动画相关API(实现选择相册、拍照、录像、动画) 文章目录 10.1选择图片wx.chooseImage(Object object)object.success 回调函数代码效果 10.2 预览图片wx.previewImage(Object object)代码效果 10.3 相机APICameraContext wx.createCameraContex…

网站提示不安全?

随着互联网的普及和发展&#xff0c;网络安全问题日益严重。黑客攻击、数据泄露、恶意软件等问题层出不穷&#xff0c;给企业和个人带来了巨大的损失。在这个背景下&#xff0c;确保网站安全显得尤为重要&#xff0c;而使用SSL证书是解决这些问题的有效措施。 什么是SSL证书&am…

mockito加junit gd 单元测试 笔记

目录 一、简介1.1 单元测试的特点1.2 mock类框架使用场景1.3 常用mock类框架1.3.1 mockito1.3.2 easymock1.3.3 powermock1.3.4 JMockit 二、mockito的单独使用2.1 mock对象与spy对象2.2 初始化mock/spy对象的方式2.3 参数匹配2.4 方法插桩2.5 InjectMocks注解的使用断言工具 三…

SSM校园学习助手系统开发mysql数据库web结构java编程计算机网页源码eclipse项目

一、源码特点 SSM 校园学习助手系统是一套完善的信息系统&#xff0c;结合springMVC框架完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用SSM框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模…

三、shell - 变量

目录 1、简介 1.1 变量的定义语法: 1.2 变量的定义需遵循的规则 1.3 变量的作用域 2、用户变量 2.1 定义变量 2.2 访问变量 2.3 变量的其他赋值方式 2.4 只读变量 2.5 删除变量 ​​​​​​​3、环境变量 ​​​​​​​3.1 常见的环境变量 ​​​​​​​3.2 自…

Logstash使用指南

介绍 Logstash是一个开源数据收集引擎&#xff0c;具有实时管道功能。它可以动态地将来自不同数据源的数据统一起来&#xff0c;并将数据标准化到你所选择的目的地。尽管Logstash的早期目标是搜集日志&#xff0c;现在它的功能已完全不只于此。任何事件类型都可以加入分析&…