【黑马程序员JVM学习笔记】02.内存结构

news/2024/4/29 3:56:01/文章来源:https://blog.csdn.net/qq_55123599/article/details/126938452

1.程序计数器

定义:
Program Counter Register 程序计数器(寄存器)

作用:
记住下一条jvm指令的执行地址

getstatic  #20    // PrintStream out = System.out;
astore_1          // --
aload_1           // out.print1n(1);
iconst_1          // --
invokevirtual #26 // --
aload_1           // out.println(2);
iconst_2          // --
invokevirtual #26 // --
aload_1           // out.println(3);
iconst_3          // --
invokevirtual #26 // --
aload_1           // out.println(4);
iconst_4          // --
invokevirtual #26 // --
aload_1           // out.println(5);
iconst_5          // --
invokevirtual #26 // --
return

特点:

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

2.虚拟机栈

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rynn34DS-1663580243335)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/89d100aa-5690-4551-8f84-236b424fbb73/Untitled.png)]
定义:
Java Virtual Machine Stacks (Java虚拟机栈)

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

问题辨析
1.垃圾回收是否涉及栈内存?
2.栈内存分配越大越好吗?
3.方法内的局部变量是否线程安全?

  • 如果方法内局部变量没有逃离方法的作用访问,它是线程安全的
  • 如果是局部变量引用了对象,并逃离方法的作用范围,需要考虑线程安全

栈内存溢出

栈帧过多导致栈内存溢出
栈帧过大导致栈内存溢出

/*演示栈内存溢出java.lang.StackOverflowError*/
public class Demo1_1 {private static int count;public static void main(String[] args) {try {method1();} catch (Throwable e) {e.printStackTrace();System.out.println(count);}}private static void method1(){count++;method1();}
}

线程运行诊断

案例1:cpu占用过多
定位

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

案例2:程序运行很长时间没有结果

3.本地方法栈

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GU2Gn8MI-1663580243337)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/cf738ca6-dc52-4849-82d8-605f93ea7e5e/Untitled.png)]

4.堆

定义:
Heap 堆

  • 通过new关键字,创建对象都会使用堆内存

特点:

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

堆内存溢出

import java.util.ArrayList;
import java.util.List;
/*
演示堆内存溢出java.lang.OutOfError:Java heap space
*/
public class Demo1_2 {public static void main(String[] args) {int i = 0;try {List<String> list = new ArrayList<>();String a = "hello";while (true) {list.add(a);a=a+a;i++;} catch (Throwable e) {e.printStackTrace();System.out.println(i);}}
}

内存诊断

  1. jps 工具
  • 查看当前系统中有哪些java进程
  1. jmap 工具
  • 查看堆内存占用情况 jmap-heap进程id
  1. jconsole 工具
  • 图形界面的,多功能的监测工具,可以连续监测
/*演示堆内存*/
public class Demo1_3 {public static void main(String[] args) throws InterruptedException {System.out.println("1...");Thread.sleep(30000);byte[] array = new byte[1024 * 1024 * 10]; // 10 MbSystem.out.println("2...");Thread.sleep(30000);array = null;System.gc();System.out.println("3...");Thread.sleep(1000000L);}
}

案例

垃圾回收后,内存占用仍然很高

5.方法区

定义

https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html

组成

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hnqF3ZVW-1663580243338)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/92bf0c2e-44a4-4895-ad1c-512720e9e4c3/Untitled.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N3tH4Uc3-1663580243339)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/203b1dd5-3c8b-442d-9f90-4c12b4cbbada/Untitled.png)]

方法区内存溢出

  • 1.8以前会导致永久代内存溢出
  • 1.8之后会导致元空间内存溢出
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.Opcodes;/**演示证元空间内存溢出*-XX:MaxMetaspaceSize=8m*/public class Demo04 extends ClassLoader{//可以用来加载类的二进制字节码public static void main(String[] args) {int j = 0;try {Demo04 test = new Demo04();for (int i = 8; i < 10000; i++ , j++) {// ClassWriter 作用是生成类的二进制字节码ClassWriter cw = new ClassWriter(0);//版本号,public,类名,包名,父类,接口cw.visit(Opcodes.V1_8,Opcodes.ACC_PUBLIC,"Class" +i,null,"java/lang/object",null);//返回 byte[]byte[] code = cw.toByteArray();//执行了类的加载test.defineClass("Class" + i,code,0,code.length);//Class 对象}}finally{System.out.println(j);}}
}

1.8以前会导致永久代内存溢出

  • 演示永久代内存溢出 java.lang.OutOfMemoryError:PermGen space
  • -XX:MaxPermSize=8m

1.8之后会导致元空间内存溢出

  • 演示元空间内存溢出 java.1ang.OutOfMemoryError:Metaspace
  • -XX:MaxMetaspaceSize=8m

场景

  • spring
  • mybatis

运行时常量池

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

StringTable

jvm-StringTable详解_爱搞技术的吴同学的博客-CSDN博客_java stringtablesize

先看几道面试题:

String s1 = "a";
String s2 = "b";
String s3 = "a" + "b";
String s4 = s1 + s2;
String s5 = "ab";
String s6 = s4.intern();
// 问
System.out.println(s3 == s4);  //false
System.out.println(s3 == s5);  //true
System.out.println(s3 == s6);  //true
String x2 = new String("c") + new String("d");//new String("cd")
String x1 = "cd";  //"cd"
x2.intern();
// 问,如果调换了【最后两行代码】的位置呢,如果是jdk1.6呢
System.out.println(x1 == x2);  //false

StringTable叫做字符串常量池,用于存放字符串常量,这样当我们使用相同的字符串对象时,就可以直接从StringTable中获取而不用重新创建对象。

字符串常量池 是 JVM 为了提升性能和减少内存消耗针对字符串(String 类)专门开辟的一块区域,主要目的是为了避免字符串的重复创建。

// 在堆中创建字符串对象"ab"
// 将字符串对象"ab"的引用保存在字符串常量池中
String aa = "ab";
// 直接返回字符串常量池中字符串对象”ab“的引用
String bb = "ab";
System.out.println(aa==bb);// true

StringTable 中保存的是字符串对象的引用,字符串对象的引用指向堆中的字符串对象。

StringTable特性

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

StringTable位置
在这里插入图片描述

StringTable垃圾回收
代码:

/**
演示 StringTable 垃圾回收
-Xmx10m -XX:+PrintStringTableStatistics -XX:+PrintGCDetails -verbose:gc
*/
public class Demo {public static void main(String[] args) throws InterruptedException {int i = 0;try {} catch (Throwable e){e.printStackTrace();}finally{System.out.println(i);}
}

StringTable性能调优
可参见博客:JVM专题(八)-StringTable调优

6.直接内存

定义

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

直接内存并不是虚拟机运行时数据区的一部分,也不是Java 虚拟机规范中定义的内存区域。在JDK1.4 中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O 方式,它可以使用native 函数库直接分配堆外内存,然后通脱一个存储在Java堆中的DirectByteBuffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。

  • 本机直接内存的分配不会受到Java 堆大小的限制,受到本机总内存大小限制
  • 配置虚拟机参数时,不要忽略直接内存 防止出现OutOfMemoryError异常

直接内存(堆外内存)与堆内存比较

  1. 直接内存申请空间耗费更高的性能,当频繁申请到一定量时尤为明显
  2. 直接内存IO读写的性能要优于普通的堆内存,在多次读写操作的情况下差异明显

代码验证:

package org.example;
import java.nio.ByteBuffer;
/*** 直接内存 与  堆内存的比较*/
public class ByteBufferCompare {public static void main(String[] args) {allocateCompare();   //分配比较operateCompare();    //读写比较}/*** 直接内存 和 堆内存的 分配空间比较* * 结论: 在数据量提升时,直接内存相比非直接内的申请,有很严重的性能问题* */public static void allocateCompare(){int time = 10000000;    //操作次数                           long st = System.currentTimeMillis();for (int i = 0; i < time; i++) {//ByteBuffer.allocate(int capacity)   分配一个新的字节缓冲区。ByteBuffer buffer = ByteBuffer.allocate(2);      //非直接内存分配申请     }long et = System.currentTimeMillis();System.out.println("在进行"+time+"次分配操作时,堆内存 分配耗时:" + (et-st) +"ms" );long st_heap = System.currentTimeMillis();for (int i = 0; i < time; i++) {//ByteBuffer.allocateDirect(int capacity) 分配新的直接字节缓冲区。ByteBuffer buffer = ByteBuffer.allocateDirect(2); //直接内存分配申请}long et_direct = System.currentTimeMillis();System.out.println("在进行"+time+"次分配操作时,直接内存 分配耗时:" + (et_direct-st_heap) +"ms" );}/*** 直接内存 和 堆内存的 读写性能比较* * 结论:直接内存在直接的IO 操作上,在频繁的读写时 会有显著的性能提升* */public static void operateCompare(){int time = 1000000000;ByteBuffer buffer = ByteBuffer.allocate(2*time);  long st = System.currentTimeMillis();for (int i = 0; i < time; i++) {//  putChar(char value) 用来写入 char 值的相对 put 方法buffer.putChar('a');}buffer.flip();for (int i = 0; i < time; i++) {buffer.getChar();}long et = System.currentTimeMillis();System.out.println("在进行"+time+"次读写操作时,非直接内存读写耗时:" + (et-st) +"ms");ByteBuffer buffer_d = ByteBuffer.allocateDirect(2*time);long st_direct = System.currentTimeMillis();for (int i = 0; i < time; i++) {//  putChar(char value) 用来写入 char 值的相对 put 方法buffer_d.putChar('a');}buffer_d.flip();for (int i = 0; i < time; i++) {buffer_d.getChar();}long et_direct = System.currentTimeMillis();System.out.println("在进行"+time+"次读写操作时,直接内存读写耗时:" + (et_direct - st_direct) +"ms");}
}

输出:
在进行10000000次分配操作时,堆内存 分配耗时:12ms
在进行10000000次分配操作时,直接内存 分配耗时:8233ms
在进行1000000000次读写操作时,非直接内存读写耗时:4055ms
在进行1000000000次读写操作时,直接内存读写耗时:745ms

可以自己设置不同的time 值进行比较

分析

从数据流的角度,来看

非直接内存作用链:
本地IO –>直接内存–>非直接内存–>直接内存–>本地IO
直接内存作用链:
本地IO–>直接内存–>本地IO

直接内存使用场景

有很大的数据需要存储,它的生命周期很长
适合频繁的IO操作,例如网络并发场景

分配和回收原理

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

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

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

相关文章

Zstack ZCCC学习笔记

ZCCC 云计算基础技术及解决方案 云计算基础概念 云计算交付方式分类 私有云&#xff1a;数据安全性高、IT基础架构可控制能力强、合规&#xff1b;公有云&#xff1a;初期投入成本低、弹性灵活&#xff1b;混合云&#xff1a;安全、合规、弹性、低成本。 服务模式分类 Ia…

文本检测DB net 实践

github 地址&#xff1a;GitHub - MhLiao/DB: A PyTorch implementation of "Real-time Scene Text Detection with Differentiable Binarization". 其他人的解析&#xff1a;DBNet阅读笔记 - 知乎 DB/DBNet&#xff1a;Real-time Scene Text Detection with Diffe…

未归一化导致Dead ReLU的悲剧

问题描述 笔者在参考http://zh.gluon.ai/chapter_deep-learning-basics/mlp-scratch.html 实现多层感知机的时候&#xff0c;遇到了一个问题 那就是&#xff0c;如果使用ReLU作为激活函数&#xff0c;模型的准确率非常低&#xff08;只有0.1&#xff09; 但是如果把那个网站上…

机器学习入门四

Octave相关资源官网地址下载地址相关语法运算符变量函数系统命令数据操作数据加载数据保存元素操作元素计算绘图和可视化工具绘图实例常用函数控制语句Octave相关资源 官网地址 官方地址 下载地址 下载地址 相关语法 运算符 %&#xff1a;注释~&#xff1a;表示不等于xo…

自学Python 62 使用urllib 包并获取百度搜索关键词中得到链接

Python 使用urllib 包 文章目录Python 使用urllib 包一、urllib 包介绍二、使用urllib.request模块三、使用urllib.parse模块在计算机网络模型中&#xff0c;Socket套接字编程属于底层网络协议开发的内容。虽然说编写网络程序需要从底层开始构建&#xff0c;但是自行处理相关协…

【图像分类】基于HOG特征结合SVM实现图像分类识别附matlab代码

1 内容介绍 ​为了满足人工智能在目标识别方法中的应用需求,需要具备对海量数据进行智能分类、识别、判读的能力.进一步挖掘了目标特性数据库数据,并将基于HOGSVM的目标识别算法应用于红外目标识别过程中.选择采集到的汽车、直升机、飞机、舰船、无人机等目标,并结合HOG算子与…

【Vite 实践】Vite 库模式能满足你吗?或许你需要统一构建

2022 年本人投入了 Vite 的怀抱&#xff0c;开始参与到 Vite 社区中&#xff0c;陆续开发了一些插件。 Vite 秉承了开箱即用&#xff0c;简化配置的思路&#xff0c;确实显著提升了前端开发体验。 但是在类库模式的构建上却有所欠缺&#xff0c;只能处理单个输入和单输入出的…

个人笔记--数据库理论 01 关系模型介绍——基于《数据库系统概念》第七版

关系模式 关系的例子 关系模型是目前广泛应用的数据模型由表的集合构成 例如 IDnamedpt_namesalary11111JAMCS12345 元组 tuple&#xff1a;表中的一行&#xff0c;元素无所谓属性 attribute : 原子的&#xff0c;不可再分的&#xff0c;要有属性域&#xff0c;如上表的nam…

云原生爱好者周刊:延迟加载任意 OCI 镜像 | 2022-09-13

开源项目推荐 SOCI Snapshotter SOCI Snapshotter 是一个 Containerd Snapshotter 插件&#xff0c;可以延迟加载任意 OCI 镜像&#xff0c;不需要 Stargz Snapshotter 一样构建特殊格式的镜像才能延迟加载。 Authentication Proxy 这个项目使用 YARP (Yet Another Reverse…

Git的认识和使用

目录 一、前置准备 二、git简介 三、gitee.com的基本使用 1.创建仓库(私库和公库) 2.创建文件及文件夹 新建文件夹两种方式 ①​ ② 3.删除 删除文件 删除仓库 四、组长组员的git使用 git clone 查看文件 git status git add git commit git push ## 命令行配置 多个…

葡聚糖-MAL/NHS/N3/Alkyne/SH/Biotin/CHO/OPSS/OH

产品名称&#xff1a; 葡聚糖-马来酰亚胺&#xff0c;葡聚糖-MAL&#xff0c;马来酰亚胺功能化葡聚糖 英文名称&#xff1a;Dextran-MAL PEG分子量可选&#xff1a;350,550,750,1k&#xff0c;2k&#xff0c;3.4k&#xff0c;5k&#xff0c;10k&#xff0c;20k&#xff08;可…

[仅需1步]企业微信群机器人[0基础接入][java]

[仅需1步]企业微信群机器人[0基础接入][java]背景介绍使用测试项目背景 公司需要把日常的服务器错误抛到企业微信群中,我正好记录下使用企业微信群机器人… 介绍 企业微信群机器人 应用介绍 企业微信是腾讯微信团队打造的企业通讯与办公工具&#xff0c;具有与微信一致的沟…

医院检验LIS系统源码

医院lis源码 实验室信息管理系统源码 .net检验系统源码 医院系统源码 了解更多源码内容&#xff0c;可私信我。 开发环境&#xff1a;.NET4.0 WPF VS2017或VS2019SQL2016 实验室信息管理系统以条码标本为主线&#xff0c;实现从采集、检测、报告、归档的全程跟踪管理。 支持…

DevOps自动化测试的原则和实践

DevOps是为了在保证高质量的前提下缩短系统变更从提交到部署至生产环境的时间。在对系统进行变更时&#xff0c;质量很重要。高质量才能让业务价值传递到系统干系人。『自动化测试既是提高质量的一种重要手段&#xff0c;也是实施持续测试必需的能力&#xff0c;因此它是DevOps…

修改WebBrowser控件的内核解决方案

首先说一下原理 当下很大浏览器他们都是用了IE的core, 这个core只提供HTML/JS的执行和渲染,并没有给出关于界面和一些特性上的事,所以开发自己浏览器如果基于IE core需要自己完成这些内容。 一张图很好的说明了这个情况,IE浏览器的架构:http://msdn.microsoft.com/en-us/li…

nginx - 负载均衡配置-负载均衡策略

目录 知识点1&#xff1a;网站流量分析指标 什么是pv&#xff1f; 什么是uv&#xff1f; 什么是IP&#xff1f; 知识点2&#xff1a;正向代理和反向代理 知识点3&#xff1a;负载均衡实验 IP地址规划&#xff1a; 实验拓扑图 知识点4&#xff1a;负载均衡策略 1、请求…

Spring5.3学习——from 官网 day1-1

Spring5.3学习——from 官网day1-1Spring5.3学习——from 官网day1-1前言概述Spring的设计理念Spring核心&#xff1a;IOC什么是IOC解释IOC容器的包什么是BeanBeanFactory接口简述ApplicationContext接口简述BeanFactory源码描述以下是Bean工厂创建和销毁bean的完整生命周期流程…

Matlab论文插图绘制模板第48期—平行坐标图(Parallelplot)

​上一期文章中&#xff0c;分享了Matlab帕累托图的绘制模板&#xff1a; 这一次&#xff0c;再来分享一种特殊的线图&#xff1a;平行坐标图。 ‘平行坐标图是一种通常的可视化方法&#xff0c;用于对高维几何和多元数据的可视化……为了克服传统的笛卡尔直角坐标系容易耗尽空…

好心情精神心理科:80%双相情感障碍被误诊,千万注意鉴别

双相情感障碍又称躁郁症&#xff0c;其表现复杂&#xff0c;容易与其他精神疾病&#xff08;包括边缘型人格障碍&#xff09;相混淆&#xff0c;超过80%的患者未能得到正确诊断。 具体如何区分双相情感障碍与边缘型人格障碍&#xff1f;在回答这个问题之前&#xff0c;好心情精…

从规模走向规模经济,锅圈食汇回归餐饮初心

预制菜源自美国&#xff0c;在日本因冷链技术发展而普及。后疫情时代&#xff0c;预制菜在中国餐饮市场加速渗透&#xff0c;成为行业的新风向。 9月&#xff0c;第一财经与CBNData发布“Growth502022中国新消费品牌年度增长力榜单”&#xff0c;预制菜品牌锅圈食汇入选。 锅…