JVM(一)——内存结构

一. 前言

1、什么是 JVM?

1)定义:
Java Virtual Machine - java 程序的运行环境(java 二进制字节码的运行环境)
2)好处:

  • 一次编写,到处运行
  • 自动内存管理,垃圾回收功能
  • 数组下标越界检查
  • 多态

3)比较:
jvm jre jdk的关系如下图
在这里插入图片描述

2、学习 JVM 有什么用 ?

  • 面试
  • 理解底层的实现原理
  • 中高级程序员的必备技能

3、常见的JVM

在这里插入图片描述

4、学习路线

在这里插入图片描述

二、内存结构

1、程序计数器

1)定义
Program Counter Register 程序计数器(寄存器)
作用,是记住下一条jvm指令的执行地址
特点

  • 是线程私有的
  • 不会存在内存溢出

2)作用

0: getstatic #20 // PrintStream out = System.out; 
3: astore_1 // -- 
4: aload_1 // out.println(1); 
5: iconst_1 // -- 
6: invokevirtual #26 // -- 
9: aload_1 // out.println(2); 
10: iconst_2 // -- 
11: invokevirtual #26 // -- 
14: aload_1 // out.println(3); 
15: iconst_3 // -- 
16: invokevirtual #26 // -- 
19: aload_1 // out.println(4); 
20: iconst_4 // -- 
21: invokevirtual #26 // -- 
24: aload_1 // out.println(5); 
25: iconst_5 // -- 
26: invokevirtual #26 // -- 
29: return
  • 解释器会解释指令为机器码交给 cpu 执行,程序计数器会记录下一条指令的地址行号,这样下一次解释器会从程序计数器拿到指令然后进行解释执行。
  • 多线程的环境下,如果两个线程发生了上下文切换,那么程序计数器会记录线程下一行指令的地址行号,以便于接着往下执行。

2、虚拟机栈

1)定义
Java Virtual Machine Stacks (Java 虚拟机栈)

  • 每个线程运行时所需要的内存,称为虚拟机栈
  • 每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存
  • 每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法

问题辨析:

  1. 垃圾回收是否涉及栈内存?
    不涉及栈内存,栈内存是在方法调用时产生的,栈帧在每次弹出栈后,会被自动回收掉。
  2. 栈内存分配越大越好吗?
    不是。因为物理内存是一定的,栈内存越大,可以支持更多的递归调用,但是可执行的线程数就会越少。
  3. 方法内的局部变量是否线程安全
    • 如果方法内部的变量没有逃离方法的作用访问,它是线程安全的-
    • 如果是局部变量引用了对象,并逃离了方法的访问,那就要考虑线程安全问题。

2)栈内存溢出

报错: java.lang.StackOverFlowError

  • 栈帧过多会导致栈内存溢出。常发生在递归调用过多,或循环引用问题
  • 栈帧过大会导致栈内存溢出
    -Xss指令可以为虚拟机栈分配内存大小。

3)线程运行诊断
案例:cpu 占用过多

  • 用top定位哪个进程对cpu的占用过高
  • ps H -eo pid,tid,%cpu | grep 进程id (用ps命令进一步定位是哪个线程引起的cpu占用过高)
  • jstack 进程id
    • 可以根据线程id 找到有问题的线程,进一步定位到问题代码的源码行号

3、本地方法栈

java 语言调用其他语言的方法或接口,实现更底层的应用和操作,本地方法栈就是用来存储的。例如 java 中的 native 关键字所引用的就是 c 或 c++ 的方法。

4、堆

1)定义
Heap 堆

  • 对于Java应用程序来说,Java堆(Java Heap)是虚拟机所管理的内存中最大的一块。Java堆是被所
    有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,Java
    世界里“几乎”所有的对象实例都在这里分配内存。

特点

  • 它是线程共享的堆中对象都需要考虑线程安全的问题
  • 有垃圾回收机制

2)堆内存溢出

报错:java.lang.OutOfMemoryError: Java heap space

-Xmx 指令可以指定堆内存大小。

3)堆内存诊断

  1. jps 工具
    查看当前系统中有哪些 java 进程
  2. jmap 工具
    查看堆内存占用情况 jmap - heap 进程id
  3. jconsole 工具
    图形界面的,多功能的监测工具,可以连续监测
  4. jvisualvm 工具

5、方法区

1)定义
方法区和堆一样,是各个线程共享的内存区域,存储了每个类的结构,例如成员变量、静态变量、方法数据、成员方法、构造器和运行时常量池。
虽然方法区逻辑上是堆的一部分,但是简单的实现可以不同,方法区只是一个规范,例如jdk8之前hotspot虚拟机的"永久代"就是方法区的实现方式之一。jdk8后,使用hotspot虚拟机使用"元空间"的方式实现方法区,也就是在本地内存来实现元空间。

2)组成
在这里插入图片描述
3)方法区内存溢出

jdk1.8之前 报错:java.lang.OutOfMemoryError: PermGen space
jdk1.8之后 报错:java.lang.OutOfMemoryError: Meta space

使用 -XX:MaxPermSize=8m 指定永久代内存大小
使用 -XX:MaxMetaspaceSize=8m 指定元空间大小
场景:

  • spring
  • mybatis

    cglib

4)运行时常量池
运行时常量池(Runtime Constant Pool)是方法区的一部分。Class文件中除了有类的版本、字
段、方法、接口等描述信息外,还有一项信息是常量池表(Constant Pool Table),用于存放编译期生
成的各种字面量与符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。

*.class 文件目录下使用javap -v ./HelloWorld.class查看二进制字节码(类基本信息,常量池,类方法定义,包含了虚拟机指令)
在这里插入图片描述
在这里插入图片描述

  • 常量池,就是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量
    等信息
  • 运行时常量池,常量池是 *.class 文件中的,当该类被加载,它的常量池信息就会放入运行时常量
    池,并把里面的符号地址变为真实地址

5)StringTable

StringTable 底层是 HashTable ,存储的字符串是唯一的,不能扩容。

常量池中的信息,都会被加载到运行时常量池中,这时 a b ab 都是常量池中的符号,还没有变为 java 字符串对象

public static void main(String[] args) {String s1 = "a";	// 默认懒加载String s2 = "b";String s3 = "ab";		// 在 StringTable 字符串常量池中创建String s4 = s1 + s2;	// new StringBuilder().append("a").append("b").toString(); => new String("ab");	在堆中创建的对象String s5 = "a" + "b";	// javac 在编译期间的优化,结果已经在编译期间确定为 ab,即 s5 == s3 -> true
}

在这里插入图片描述

6)StringTable 的特性

  • 常量池中的字符串仅是符号,第一次用到时才变为对象
  • 利用串池的机制,来避免重复创建字符串对象
  • 字符串变量拼接的原理是 StringBuilder (1.8)
  • 字符串常量拼接的原理是编译期优化
  • 可以使用 intern 方法,主动将串池中还没有的字符串对象放入串池
    • 1.8 将这个字符串对象尝试放入串池,如果有则直接返回串池中的对象,如果没有则放入串池, 会把串池中的对象返回
    • 1.6 将这个字符串对象尝试放入串池,如果有则直接返回串池中的对象,如果没有会把此对象复制一份,放入串池, 会把串池中的对象返回

例1:

// 在 jdk1.8中
public class Main{public static void main(String[] args) {// 串池StringTable["a", "b"]// 堆 new String("a"), new String("b"), new StringBuilder() * 3, new String("ab")String s = new String("a") + new String("b");	//一共创建了 6 个对象// TODO: 字符串对象s调用intern()方法, 由于串池中没有"ab",将 s 放入串池中,直接返回串池中的对象String s2 = s.intern();		String x = "ab";	// 取出串池中的 "ab"System.out.println( s2 == x);	// trueSystem.out.println( s == x );	// true}
}

例2:

// 在 jdk1.6中
public class Main{public static void main(String[] args) {// 串池StringTable["a", "b"]// 堆 new String("a"), new String("b"), new StringBuilder() * 3, new String("ab")String s = new String("a") + new String("b");	//一共创建了 6 个对象// TODO: 字符串对象s调用intern()方法, 由于串池中没有"ab", 字符串对象s会被复制一份放到串池中,返回串池中的对象String s2 = s.intern();		String x = "ab";		// 取出串池中的 "ab"System.out.println( s2 == x);	// trueSystem.out.println( s == x );	// false}
}

7)StringTable的位置

jdk1.6 StringTable 位置是在永久代中,1.8 StringTable 位置是在堆中。

8)StringTable垃圾回收
添加虚拟机参数:-Xmx10m -XX:+PrintStringTableStatistics -XX:+PrintGCDetails -verbose:gc

public class Demo1_7 {public static void main(String[] args) throws InterruptedException {int i = 0;try {for (int j = 0; j < 10000; j++) { // j=100, j=10000String.valueOf(j).intern();i++;}} catch (Throwable e) {e.printStackTrace();} finally {System.out.println(i);}}
}
[GC (Allocation Failure) [PSYoungGen: 2048K->496K(2560K)] 2048K->720K(9728K), 0.0015984 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
10000 	# 10000个字符被GC回收了一部分
...
StringTable statistics:
Number of buckets       :     60013 =    480104 bytes, avg   8.000
Number of entries       :      5671 =    136104 bytes, avg  24.000
Number of literals      :      5671 =    346048 bytes, avg  61.021	# 字符串常量池中的字符串
Total footprint         :           =    962256 bytes

9)StringTable性能调优

如果需要添加的字符串常量的数量很多,可以适当增加HashTable桶的个数,来减少字符串放入串池所需要的时间。

-XX:StringTableSize=桶个数(最少设置为 1009 以上)
  • 考虑将字符串对象是否入池
    • 可以通过 intern 方法减少重复入池

6、直接内存

1)定义

  • 常见于 NIO 操作时,用于数据缓冲区
  • 分配回收成本较高,但读写性能高
  • 不受 JVM 内存回收管理

2)分配和回收原理

  • 使用了 Unsafe 对象完成直接内存的分配回收,并且回收需要主动调用 freeMemory 方法
  • ByteBuffer 的实现类内部,使用了 Cleaner (虚引用)来监测 ByteBuffer 对象,一旦
    ByteBuffer 对象被垃圾回收,那么就会由 ReferenceHandler 线程通过 Cleaner 的 clean 方法调
    用 freeMemory 来释放直接内存。

在进行JVM调优时,尝试用-XX:+DisableExplicitGC指令,可以防止我们自己手动的进行垃圾回收 (System.gc())。
但是在直接内存中,JVM无法自动对直接内存进行垃圾回收,我们可以通过 Unsafe 中的 freeMemory 方法手动释放内存

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

C++从入门到精通——函数重载

函数重载 前言一、函数重载概念二、函数重载的分类参数类型不同的函数重载参数个数不同的函数重载参数类型顺序不同的函数重载 三、函数重载的具体代码展示main.cpp 四、为什么为什么C支持函数重载&#xff0c;而C语言不支持函数重载呢 前言 函数重载是指在同一个作用域内&…

.NET CORE 分布式事务(三) DTM实现Saga及高并发下的解决方案

目录(结尾附加项目代码资源地址) 引言&#xff1a; 1. SAGA事务模式 2. 拆分为子事务 3. 失败回滚 4. 如何做补偿 4.1 失败的分支是否需要补偿 5. 异常 6. 异常与子事务屏障 6.1 NPC的挑战 6.2 现有方案的问题 6.3 子事务屏障 6.4 原理 7. 更多高级场景 7.1 部分…

30-3 越权漏洞 - 水平越权(横向越权)

环境准备:构建完善的安全渗透测试环境:推荐工具、资源和下载链接_渗透测试靶机下载-CSDN博客 一、定义 攻击者可以访问和操作与其拥有同级权限的用户资源。 示例: 学生A在教务系统上正常只能修改自己的作业内容,但由于不合理的权限校验规则等原因,学生A可以修改学生B的内…

element-ui autocomplete 组件源码分享

紧接着 input 组件的源码&#xff0c;分享带输入建议的 autocomplete 组件&#xff0c;在 element-ui 官方文档上&#xff0c;没有这个组件的 api 目录&#xff0c;它的 api 是和 input 组件的 api 在一起的&#xff0c;看完源码之后发现&#xff0c;源码当中 autocomplete 组件…

Linux部分命令

目录 1.文件介绍 2.ls命令 3.目录命令 4.相对路径以及绝对路径 5.命令创建目录&#xff08;文件夹&#xff09; 6.which命令 7.find命令 8.grep命令 9.wc命令 10.echo、tail、重定向符 1.文件介绍 和window不同&#xff0c;Linux没有盘路径&#xff0c;所有的文件都存…

Linux部署Kafka2.8.1

安装Jdk 首先确保你的机器上安装了Jdk&#xff0c;Kafka需要Java运行环境&#xff0c;低版本的Kafka还需要Zookeeper&#xff0c;我此次要安装的Kafka版本为2.8.1&#xff0c;已经内置了一个Zookeeper环境&#xff0c;所以我们可以不部署Zookeeper直接使用。 1、解压Jdk包 t…

OSPF GTSM(通用TTL安全保护机制)

目录 GTSM的定义 使用GTSM的目的 GTSM的原理 配置OSPF GTSM实例 组网需求 配置思路 操作步骤 1. 配置各接口的IP地址 2.配置OSPF基本功能 3.配置OSPF GTSM 4. 验证配置结果 GTSM的定义 GTSM&#xff08;Generalized TTL Security Mechanism&#xff09;&#xff0c;…

1.Mysql基础入门—MySQL-mysql 8.0.11安装教程

1.Mysql基础入门—MySQL-mysql 8.0.11安装教程 摘要个人简介下载Mysql安装Mysql配置环境变量 摘要 MySQL 8.0.11的安装过程涉及几个关键步骤&#xff0c;首先访问MySQL官方网站下载页面&#xff0c;选择操作系统相对应的MySQL版本进行下载。对于Windows用户&#xff0c;启动下…

element-ui inputNumber 组件源码分享

今日简单分享 inputNumber 组件的实现原理&#xff0c;主要从以下四个方面来分享&#xff1a; 1、inputNumber 组件的页面结构 2、inputNumber 组件的属性 3、inputNumber 组件的事件 4、inputNumber 组件的方法 一、inputNumber 组件的页面结构。 二、inputNumber 组件的…

【动手学深度学习-pytorch】-9.3深度循环神经网络

到目前为止&#xff0c;我们只讨论了具有一个单向隐藏层的循环神经网络。 其中&#xff0c;隐变量和观测值与具体的函数形式的交互方式是相当随意的。 只要交互类型建模具有足够的灵活性&#xff0c;这就不是一个大问题。 然而&#xff0c;对一个单层来说&#xff0c;这可能具有…

霸榜京东数据库图书热卖榜!《图数据库:理论与实践》热销中

《图数据库&#xff1a;理论与实践》自2月上市以来&#xff0c;受到了数据库行业的广泛关注与热烈支持&#xff0c;问世两周便销量破千本&#xff01;近期还荣登京东 “数据库图书榜”热卖榜第二名&#xff0c;广获好评&#xff01; 在此&#xff0c;真挚的感谢各位读者的认可…

IP地址暴露可能带来的风险和危害

当自己的IP地址暴露时&#xff0c;可能会面临一系列的风险和潜在危害。IP地址作为互联网上连接用户与网络设备的标识符&#xff0c;其安全性对于个人信息安全至关重要。以下将详细探讨IP地址暴露可能带来的后果&#xff0c;并提出相应的防范措施。 首先&#xff0c;IP地址暴露可…

docker logs 查找日志常用命令

docker logs 是什么 docker logs 是 Docker 命令行工具提供的一个命令&#xff0c;用于查看容器的日志输出。它可以显示容器在运行过程中生成的标准输出&#xff08;stdout&#xff09;和标准错误输出&#xff08;stderr&#xff09;&#xff0c;帮助用户诊断容器的行为和排查…

LVS几种模式介绍

备注&#xff1a;这篇真的是水文&#xff0c;不看也罢。 LVS&#xff0c;linux virtual server&#xff0c;可提供IP网络层的负载均衡。 其主要模式主要有以下几种&#xff1a; LVS-NAT 主要通过网络地址转换&#xff0c;修改目的IP实现。Network Address Translation LVS-…

关于Oracle VM VirtualBox无法查询IP地址的原因

1.如下&#xff0c;输入ifconfig却没有显示我框住的显示IP。 2.原因有可能&#xff1a; &#xff08;1&#xff09;主机没连上网络。 &#xff08;2&#xff09;虚拟机网络设置不正确。

bugku-web-你必须让他停下

这里在不停的刷新 页面源码 开发脚本截取同样效果 利用抓包工具将其请求与得到的数据全部记录下来 发现只有10.jpg可以被正常加载出来 将其图片打开 发现没有flag 再将其转化为txt文件 同样不对 这时回到抓包处 查看请求10.jpg文件的报文 得到flag

公司服务器被.rmallox攻击了如何挽救数据?

公司服务器被.rmallox攻击了如何挽救数据&#xff1f; .rmallox这种病毒与之前的勒索病毒变种有何不同&#xff1f;它有哪些新的特点或功能&#xff1f; .rmallox勒索病毒与之前的勒索病毒变种相比&#xff0c;具有一些新的特点和功能。这种病毒主要利用加密技术来威胁用户&am…

HarmonyOS 应用开发之ExtensionAbility组件

ExtensionAbility组件是基于特定场景&#xff08;例如服务卡片、输入法等&#xff09;提供的应用组件&#xff0c;以便满足更多的使用场景。 每一个具体场景对应一个 ExtensionAbilityType&#xff0c;开发者只能使用&#xff08;包括实现和访问&#xff09;系统已定义的类型。…

什么是广告可见性测量 如何测量广告可见性

广告可见性测量 & 如何测量广告可见性 --- 一起来来认识MOAT 现在是2024年&#xff0c;大家或许还记得大约8年前广告可见性&#xff08;Ad viewability&#xff09;成为数字媒体世界的一种货币以来&#xff0c;出版商一直处于不利地位。当广告商用不同的工具和技术武装自…

千川素材投放效果追踪与精准识别:从数据洞察到策略优化的全面跃升

一、数据驱动的投放效果追踪在数字化营销时代&#xff0c;数据的力量不可忽视。对于广告主而言&#xff0c;投放效果追踪不仅是对广告效果的简单度量&#xff0c;更是对市场反应、用户行为和广告策略的深入分析。通过数据驱动的投放效果追踪&#xff0c;广告主可以更加精准地了…