首页 > 系统相关 >JVM:内存结构上

JVM:内存结构上

时间:2023-04-13 20:24:48浏览次数:34  
标签:lang ldc String 线程 内存 JVM 结构 常量

jvm内存结构·上

程序计数器

虚拟机栈(线程私有栈-方法的栈帧\FILO)

  • 当前线程的压入的方法的栈帧
  • -Xss"SIZE": 设置线程栈大小

栈内存溢出

  • SO:StackOverflowError at '错误代码处'
  • 递归调用
  • 有时第三方库也会导致SO:ObjectMapper / @JsonIgnore

线程安全

  • 方法的局部变量/堆对象是否有可能被多个线程共享
  • 非线程安全情况:
    局部变量的引用来自于方法的参数或方法的返回值

线程诊断(栈诊断工具:jstack)

  • linux环境:
  • nohup 进程(路径)名

  • top :用top定位哪个进程对cpu占用过高

  • ps H -eo pid,tid,%cpu | grep '十进制进程id'
    :用ps命令进一步定位 哪个线程 引起的cpu占用过高

  • jstack '十进制进程id' ->转16进制(32665 -> 0x7f99)
    nid = 0x7f99
    观察线程状态:java.lang.thread.state:runable/timed_waiting at 错误行

  • jstack -> 检测
    死循环(一直runable)、对象线程同步锁死锁(一直block)

本地方法栈(native方法(NM)的栈)

  • 本地方法是用C/C++编写的java方法
    为了更好的和操作系统 进行交互
  • 给NM的运行提供内存空间

  • 线程创建的对象
  • 线程共享,要考虑线程安全
  • 垃圾回收

堆内存溢出

  • OOM:OutOfMemoryError: java heap space / at '错误代码处'
  • OOM原因:一直在创建对象,并且不进行GC
  • -Xmx'size'
  • Space: Eden、From、To、PS Old Generation
  • Space Use: capacity、used、free(capacity=used+free)

堆内存诊断

  • jps:查看本地java进程pid
  • jmap -heap 'pid'
    切点式检测
  • jconsole
    连续监测,很直观,功能更丰富

案例:gc后内存占用率仍很高

如何使用监测工具进行排查

  • 问题点:PS Old Generation堆内存无法被GC回收
  • 思考问题:是因为编程失误导致对象一直被引用吗?
  • **监测工具:jvisualvm (可视化方式展示jvm)
  • jvisualvm--监视--堆dump:保存具体时刻的堆的快照
  • 堆dump--检查--保留大小最大的对象
  • **总结:实际情况也是,先堆转储/堆dump,然后检查最大的对象们

方法区:MethodArea

  • 定义:.text段
    包括:run-time常量池、成员变量和方法数据,方法或构造器代码
  • 不同的jvm厂商实现MA的方式可能不同
    有的使用 heap的一部分 ,有的 使用操作系统提供的本地内存
  • OOM:MA内存溢出

MA内存溢出

  • 1.8前:永久代内存溢出
  • 1.8后:元空间内存溢出

jvm内存结构·中

方法区:MethodArea(概念)|永久代或元空间(实现)

  • 定义:.text段+.rodata段+
    包括:Class、ClassLoader、常量池(constant pool)、运行时常量池
  • 不同的jvm厂商实现MA的方式可能不同
    有的使用 heap的一部分PermGen ,有的 使用操作系统提供的本地内存Metaspace
  • OOM:MA内存溢出

MA内存溢出

  • 1.8前:永久代内存溢出
  • 报错信息:OutOfMemory:PermGen
  • VM模拟:-XX:MaxPermGenSize=8m
  • 场景模拟:(asm)ClassWrite-.visit-.toArrayByte-.defineClass
  • 实际场景:实际场景经常需要动态的类加载
    比如spring和mybatis需要生成代理类,使用不当就会导致MA的OOME
  • 动态生成类、动态加载类的场景非常多,不要轻视
  • 1.8后:元空间内存溢出

未加载的类字节码 / (字节码的)常量池(CP)

  • 反编译字节码工具:
    javap -v '类名'.class
    内容 :
    1. 类基本信息
      • 类文件路径:Classfile
      • 签名:MD5 checksum
      • 最后修改:Last Modified
      • 类版本:minor/major version(0-51)
      • 类权限标志:flags: ACC_PUBLIC、ACC_SUPER、···
    2. 常量池:constant pool
    3. 类的方法定义
      • 实例:sout."hello world"的包装方法的字节码
      • public static void main(java.lang.String[]);
        descriptor: (···)
        flags: (0x0009) ACC_PUBLIC、ACC_STATIC
        Code: stack=2, locals=1, args_size=1
        0: getstatic #2--Flied java.lang.System.out
        3: ldc #3--String hello world
        5: invokevirtual #4 --Method java.io.P-S-.println
        8: return
      • 解析:
        1. 获得字节码中'#n'(符号地址)
          找到在常量池中的具体值

          #2: Fieldref #21,#22
          • #21: Class #28
            #28: Utf-8 java/lang/System
          • #22: NameAndType #29:#30
            #29: Utf-8 out
            #30: Utf-8 Ljava/io/P-S-
          #3: String #23
          #23: Utf-8 hello world
        2. 执行完整的字节码指令

加载后字节码 / 运行时常量池(RTCP)

  • 运行时常量池(RTCP)的定义:
    类字节码被加载后,常量池CP将被加载到JVM的运行时常量池中
    (其中符号位被替换成真实的对象)
  • String Table(串池 / ST): (只存在于RTCP,字节码的常量池不存在串池 / ST)
    StringTable本质是一个JVM的哈希表结构的内存
    1. 串池懒/延迟创建常串对象

      • 例子:
        ()->{

            str1 = "a";
            str2 = "b";
            str3 = "ab";
        }
      • 编译后查看字节码:
        0: ldc #2-- // 加载RTCP中String a符号,使其成为字符串对象
        2: astore_1--//将加载的"a"存储到slot1(str1)
        // ldc #2 会把RTCP的a符号变成串池中具体的"a"字符串对象
        // 过程:ldc会检查a符号串作为key,检查"a"是否存在于串池,
        // 如果不存在则创建"a",并将"a"加入串池
        3: ldc #3--//加载String b
        5: astore_2--//将加载的"b"存储到slot1(str2)
        ldc#4,astore_3--加载String ab,将加载的ab存储到slot1(str3)
        LocalVariableTable:(局部变量表)
        Start  Length   Slot  Name  Signature:  
          0  10  0 args   [Ljava/lang/String;
          3  7  1 str1   Ljava/lang/String;
          6  4  2 str2   Ljava/lang/String;
          9  1  3 str3   Ljava/lang/String;
      • 总结:懒的:
        并不是执行前就在串池ST中创建字符串对象;
        当执行到代码时,才进行串池ST对目标字符串对象的判断、创建和加载
        查询StringTable中是否有该串,有-返回对象,没有-添加
    2. 串变量拼接

      • 例子:
        ()->{
            str1 = "a"; // ldc "a",astore_1
            str2 = "b"; // ldc "b",astore_2
            str3 = "ab"; // ldc "ab",astore_3
            str4 = str1 + str2; // new StringBuilder().append(str1///"a").append(str2///"b").toString();
        // 这里的 StringBuilder 的 toString 方法 = new String(sb.value, 0 ,sb.length)
        // new String()产生一个堆中的字符串对象"ab",而不是串池中存在的"ab" }
    3. 串常量拼接(编译期优化)

      ()->{
          str1 = "a"; // ldc "a",astore_1
          str2 = "b"; // ldc "b",astore_2
          str3 = "ab"; // ldc "ab"#4,astore_3
          str4 = "a" + "b"; // ldc "ab"#4,astore_4
      //这里直接引用了str3引用的串池中的"ab"
      //编译器会对常量对象的运算进行优化
    4. 串池懒创建串字面量

      ()->{
          sout."1"; // 常量池字符串数量 101
          sout."2"; // 常量池字符串数量 102
          sout."3"; // 常量池字符串数量 103
          sout."1"; // 常量池字符串数量 103
          sout."2"; // 常量池字符串数量 103
      //这里直接引用了str3引用的串池中的"ab"
      //编译器会对常量对象的运算进行优化
    5. ST.intern

      ()->{String s = new String ("a") + new String("b");}
      字节码的描述:
      String var10000 = new String("a");
      String s = var10000 + new String("b");
      System.out.println();
      字节码源码:
      Code:
      stack=4, locals=2, args_size=1
      0: new #2 // class java/lang/String
      3: dup
      4: ldc #3 // String a
      6: invokespecial #4 // Method java/lang/String."<init>":(Ljava/lang/String;)V
      9: new #2 // class java/lang/String
      12: dup
      13: ldc #5 // String b
      15: invokespecial #4 // Method java/lang/String."<init>":(Ljava/lang/String;)V
      18: invokedynamic #6, 0 // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;

标签:lang,ldc,String,线程,内存,JVM,结构,常量
From: https://www.cnblogs.com/lovecodingforever/p/17316251.html

相关文章

  • Linux程序内存释放不回收问题
    最近在测试一个程序,内存表现很诡异,记录一下。测试环境:vmware虚拟机,操作系统:ubuntu16.04x64程序中有一段业务需要申请大块内存,业务完成后再释放掉。测试表现:程序启动占用内存约90M,执行一次业务后,内存增长约200M,且不回落,这样约5次后,内存增长到约1G后趋于稳定。开始感觉有内存......
  • Redis基础数据结构
    五种基础数据结构:string(字符串)、list(列表)、set(集合)、hash(集合)和set(有序集合)使用命令redis-cli即可连接使用go语言代码连接redis:import( "github.com/go-redis/redis")varc*redis.Clientfuncmain(){ c=redis.NewClient(&redis.Options{ Addr:......
  • 关于前端基础数据结构的问题
    常用的数据集采用数组的好处,当然对于前端新人来很容易混淆,如下的数据是数组(js的数组本就是特殊的对象,因此又叫数组对象)由于这缘故很多网上的叫法五花八门所以下面的数据结构很容易混淆,以为这是数组对象(其实这样叫没错,只是理解成是真对象(js的数组也是对象的一种,先区别一下免得混淆......
  • jvm
    java内存模型与分区:本地方法栈:native方法调用本地其他语言接口;程序计数器:记录当前线程的运行位置;栈:存放运行时的方法,包括:局部变量表、操作数栈、动态链接(指向常量池)和方法返回地址。堆:初始化的对象、成员变量。方法区:类型信息(加载类的类型(Class)、类的完整名称、类型修饰符等......
  • JVM 内存
    简述JVM内存模型  从宏观上来说JVM内存区域分为三部分线程共享区域、线程私有区域、直接内存区域。1.1、线程共享区域1.1.1、堆区堆区Heap是JVM中最大的一块内存区域,基本上所有的对象实例都是在堆上分配空间。堆区细分为年轻代和老年代,其中年轻代又分为Eden、S0、S1......
  • 论结构化、系统性的学习
    在大的工作环境以及普遍的生活压力下。对以后充满了迷茫。尤其是30多岁以后的人生。中年的危机与焦虑如何避免?职场的规划与路线怎么制定?生活的压力与焦灼如何解决?家庭的压力.....其实主要还是职场的规划。人,一般来说,对于百分之九十九以上的人,都是要工作的。那么在国内这样......
  • [转载]php递归生成树形结构(几种常见的数据结构)
    版权声明:本文为CSDN博主「陈文焕」的原创文章,遵循CC4.0BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/qq_23116221/article/details/109910846pid找上级id$array=array(array('id'=>1,'pid'=>0,'n'=>'河北省'),ar......
  • go语言基础-基本数据结构
    0x00基本数据结构go语言中,除了基本的整型、浮点型、布尔型、字符串外,还有数组、切片、结构体、函数、map、通道(channel)等。0x00整型(int)整型分为以下两个大类:按长度分为:int8、int16、int32、int64对应的无符号整型:uint8、uint16、uint32、uint64。其中,uint8就是我们熟知的......
  • 向下取整内存与运行时间
    Math.floor虽然为js的内置函数库,但还是会存在调用函数库的问题,但parseInt为javascript的内置函数,并不会存在调用函数库这种操作,所以就结果而言使用parseInt向下取整会比Math.floor占用更少的内存以及更快的运行时间。使用parseInt使用Math.floor......
  • 记首次用Cheat Engine修改游戏内存
    记得第一次见到ce修改器还是因为听闻ce修改器可以修改百度网盘下载速度,但是测试了一下发现确实可行(虽然不知道现在修复了没)。最近想起来得好好研究它的功能。最后我发现了了一片讲的很细的CE修改器教程入门篇,作者用ce自带的一个练习程序详细的讲解了几种修改方式。学习之后为了实......