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://www.mzph.cn/news/779075.shtml

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

相关文章

低压MOS在新能源园林机械上的应用-REASUNOS瑞森半导体

一、前言 在欧美地区&#xff0c;以锂电池为动力源的新能源园林机械迅速地替代着以往的燃油和交流电动力机器。而中国也将迎来一场风暴式革命。 园林工具是人类绿化景观的养护设备&#xff0c;是以养护草坪、绿篱、保护花草、树木为作业对象的&#xff0c;代替大部分手工劳动…

国内ip代理速度快的秘密

在互联网时代&#xff0c;IP代理已经成为许多网络用户、企业和开发者的重要工具。而在国内&#xff0c;由于网络环境的复杂性和特殊性&#xff0c;寻找一个速度快、稳定可靠的IP代理显得尤为重要。虎观代理将深入探讨国内IP代理速度快的秘密&#xff0c;并分析其带来的优势和应…

多个微信这样高效管理

随着微信成为企业商务沟通的主要平台&#xff0c;一些业务咨询量较大的行业&#xff0c;如教育培训、旅游、美容及医疗等&#xff0c;通过微信开展营销活动和客户服务过程中&#xff0c;经常面临多微信管理难题。 在这种情况下&#xff0c;采用微信线上业务模式&#xff0c;需…

Kubernetes篇(一)— kubernetes介绍

目录 前言一、应用部署方式演变二、kubernetes简介三、kubernetes组件四、kubernetes概念 前言 本章节主要介绍应用程序在服务器上部署方式演变以及kubernetes的概念、组件和工作原理。 一、应用部署方式演变 在部署应用程序的方式上&#xff0c;主要经历了三个时代&#xff…

雷军分享造车故事:储备1363亿元的现金,吊打特斯拉Model 3

小米召开新车发布会&#xff0c;正式发布小米 SU7。该车定位中大型纯电轿车&#xff0c;有 SU7、SU7 Pro、SU7 Max 三个版本&#xff0c;车身尺寸 4997/1963/1455mm&#xff0c;轴距 3000mm。售价 21.59-29.99 万。 在小米汽车SU7发布会后&#xff0c;小米集团的创始人、董事长…

Ubuntu 系统下安装 Nginx

目录 一、Nginx是什么 ​二、Ubuntu 系统下安装 Nginx 1、安装包下载 2、上传服务器并解压缩 3、依赖配置安装 4、生成编译脚本 ​5、编译 6、开始安装 7、设置为随机自启动 7.1、创建 nginx.service 文件&#xff0c;将以下内容粘贴到文件中 7.2、将 nginx.service…

增长超500%!亚马逊卖疯的旅行箱,赛盈分销浅析今年企业出海布局方向!

箱包行业迎来了新的发展契机&#xff0c;一方面是在工艺与技术创新下&#xff0c;另一方面&#xff0c;旅游经济复苏的推动下&#xff0c;全球箱包行业取得飞速发展。 Euromonitor & 华泰研究针对2018-2028这十年间的箱包市场进行了调研&#xff0c;数据显示2023年全球箱包…

Filter、Listener、AJAX

Filter 概念&#xff1a;Filter 表示过滤器&#xff0c;是JavaWeb三大组件(Servlet、Filter、 Listener)之一。 过滤器可以把对资源的请求拦截下来&#xff0c;从而实现一些特殊的功能。 过滤器一般完成一些通用的操作&#xff0c;比如&#xff1a;权限控制、统一编码处理、敏感…

Parallels Desktop 18中文--体验无缝的跨平台工作,轻松驾驭Windows与Mac

Parallels Desktop 18是一款功能强大的虚拟机软件&#xff0c;专为Mac用户设计&#xff0c;能在Mac上流畅运行Windows和其他操作系统。它提供了更快的性能、更好的图形处理以及更简洁的虚拟机导入导出功能&#xff0c;还支持Apple M1芯片&#xff0c;优化性能和电池续航时间。P…

从创意立项到产品赚钱的全调优过程复盘,如何提高产品存活率 | TopOn变现干货

10月28日&#xff0c;由TopOn、罗斯基联合主办的“游戏赛道新机会”主题沙龙在成都举办。活动邀请了国内外多位知名公司及游戏爆款产品的负责人分享&#xff0c;分别从各自的方向及经验出发&#xff0c;以数据、案例、产品分析、行业趋势等多个维度&#xff0c;为行业从业者带来…

RGB,深度图,点云和体素的相互转换记录

目录 1.RGBD2Point 1.2 步骤 2.Point2Voxel-Voxelization 2.1 原理 2.2 代码 3.Voxel2Point 4.Point2RGB 5.Voxel2RGB 1.RGBD2Point input&#xff1a;RGB D 内外惨 output&#xff1a;points cloud def depth2pcd(depth_img):"""深度图转点云数据图…

鸿蒙开发人才紧缺!这份《HarmonyOS教学视频》帮你更快上手鸿蒙

去年9月&#xff0c;华为宣布鸿蒙原生应用全面启动&#xff0c;基于开源鸿蒙开发的 HarmonyOS NEXT 鸿蒙星河版将在今年秋天正式和消费者见面。该版本系统底座将由华为全线自研&#xff0c;去掉传统安卓 AOSP 代码。 这意味着&#xff0c;鸿蒙星河版将不再兼容安卓应用&#xf…

47 vue 常见的几种模型视图不同步的问题

前言 这里主要是来看一下 关于 vue 中的一些场景下面 可能会出现 模型和视图 不同步更新的情况 然后 这种情况主要是 vue 中的对象 属性没有响应式的 setter, getter 然后 我们这里就来看一下 大多数的情况下的一个场景, 和一些处理方式 当然 处理方式主要是基于 Vue.set, …

Redis命令-Key的层级结构

基础篇Redis 4.4 Redis命令-Key的层级结构 Redis没有类似MySQL中的Table的概念&#xff0c;我们该如何区分不同类型的key呢&#xff1f; 例如&#xff0c;需要存储用户.商品信息到redis&#xff0c;有一个用户id是1&#xff0c;有一个商品id恰好也是1&#xff0c;此时如果使…

你不知道的Python

Python&#xff0c;作为一种广泛使用的高级编程语言&#xff0c;因其易于学习和强大的库支持而受到开发者的青睐。尽管如此&#xff0c;Python 仍有许多鲜为人知的特性和技巧&#xff0c;这些隐藏的宝藏可以让编程工作变得更加高效和有趣。本文将揭示一些“你不知道的Python”特…

go发布包到github

1. 首先&#xff0c;我们在github上创建一个公有仓库并clone到本地 git clone https://github.com/kmust-why/gdmp-token.git cd gdmp-token/ 2. 在gdmp-token工程中初始化go.mod&#xff0c;其中后面的链接要跟github上创建的仓库和你的用户名对应 go mod init github.com…

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;…

linux安装Zookeeper的详细步骤

1.Java环境确认 确保已经安装了Java环境&#xff0c;没有的自行安装 2.官网下载包 Apache ZooKeeper 3.安装 3.1上传到linux&#xff0c;解压 我的目录为/root/apache-zookeeper-3.8.4-bin 进入到/root/apache-zookeeper-3.8.4-bin/conf目录下&#xff0c;执行命令复制zoo…

uniapp实现列表动态添加

1.效果图&#xff1a; 2.代码实现&#xff1a; 这里没有用uniapp提供的uni-list控件 <template> <view id"app"> <!-- 这里为了让标题&#xff08;h&#xff09;居中展示&#xff0c;给h标签设置了父标签&#xff0c;并设置父标签text-…

RK3568-开启ptp服务

硬件支持 mac或者phy需要支持ptp驱动支持 CONFIG_PTP_1588_CLOCK=y虚拟机端:虚拟机只支持软件时间戳。 安装ptp服务:sudo apt-get install linuxptpbuildroot系统-开发板端:开发板支持硬件时间戳和软件时间戳。 BR2_PACKAGE_LINUXPTP=y 编译相关ptp4l程序ubuntu系统-开发…