Java内存区域学习笔记

发布时间 2023-03-22 21:15:34作者: 哦、菜狗啊

image-20230320112755955

image-20230320112420138

 

源码 → 二进制字节码 → 解释器 → 机器码 → CPU

  1. 程序计数器(线程私有):记住下一条jvm指令执行地址,解释器找到该条指令解释成机器码,运,如此往复

  2. Java Virtual Machine Stacks (Java 虚拟机栈)

    每个线程运行时所需要的内存,称为虚拟机栈

    每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存

    每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法

    线程私有,存放一个方法的局部变量、操作数栈、常量池指针。局部变量表(基本数据类型、对象引用、returnAddress类型)

    规定两种异常:StackOverflowError(线程请求的栈深度大于允许的深度)OutOfMemoryError(无法申请到足够内存)

  3. 本地方法栈 为虚拟机调用本地方法,与虚拟机栈类似,异常也相同,HotSpot就将其与虚拟机栈合并

  4. 堆 (-Xms8m,设置堆大小) 通过 new 关键字,创建对象都会使用堆内存,几乎所有对象实例以及数组都在堆上分配

    它是线程共享的,堆中对象都需要考虑线程安全的问题

    有垃圾回收机制,GC堆(Garbage Collected Heap)

    逻辑上连续,物理上不连续

    1. jps 工具

    查看当前系统中有哪些 java 进程

    1. jmap 工具

    查看堆内存占用情况 jmap - heap 进程id

    1. jconsole 工具

    图形界面的,多功能的监测工具,可以连续监测

    1. jvisualvm

    Java虚拟机监控工具,对JVM的性能进行监控

  5. 方法区 各个线程共享的内存区域,储存已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据

  6. 运行时常量池(Runtime Constant Pool)

    image-20230320160425207

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

    常量池,就是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等信息

    运行时常量池,常量池是 *.class 文件中的,当该类被加载,它的常量池信息就会放入运行时常量池,并把里面的符号地址变为真实地址

    • 为什么需要常量池?

    一个java源文件中类、接口、编译后产生一个字节码文件。而Java 中的字节码需要数据支持,通常这种数据会很大以至于不能直接存到字节码里,换另一种方式,可以存到常量池,这个字节码包含了指向常量池的引用。在动态链接的时候会用到运行时常量池

StringTable 位于堆中,new String("a"),new String("b")此时a,b对象在堆中,并在StringTable生成["a","b"],比如String s = new String("a")+new String("b"),此时StringTable中有["a","b"],堆中有a、b、ab三个对象

String中的intern方法是一个 native 的方法,当调用 intern方法时,如果串池已经包含一个等于此String对象的字符串,则返回池中的字符串。否则,将该字符串对象的引用放入串池,并该字符串的引用。(在1.6中不同,是复制一份放过去,所以串池中的跟对象的会不同),例子:

String s3 = new String("1") + new String("1");    
s3.intern();    
String s4 = "11";    
System.out.println(s3 == s4); //1.6 false, 1.8 true

使用intern可以将堆中的ab对象加入StringTable中,String s2 = s.intern(),此时StringTable中有["a","b","ab"],s2等于StringTable中的"ab"

总之,String s = new String("a") 会在串池中生成a(由char数组构成),然后池外也创建a,池外的a对象中value指向串池中的a的char数组,s指向的是池外的对象,而s.intern()会因为串池中有a会返回串池中的a,此时s.intern()=="a"为true。而当字符串对象只在堆中存在时执行intern(),将该对象的引用放入串池中。 注意,拼接时有变量相当于用StringBuilder在堆中创建对象,串池中没有。

参考:https://blog.csdn.net/hqweay/article/details/88753035 https://blog.csdn.net/weixin_42073629/article/details/116378067 也可以添加参数 -XX:+PrintStringTableStatistics 查看StringTable里添加的字符串个数。 参考https://blog.csdn.net/m0_46340286/article/details/126102636

-XX:StringTableSize=桶个数

  1. 直接内存 -XX:MaxDirectMemorySize 默认则与Java堆最大值一致

    常见于 NIO 操作时,用于数据缓冲区 基于通道与缓冲区的I/O方式,可使用Native函数库直接分配堆外内存 分配回收成本较高,但读写性能高 不受 JVM 内存回收管理

  • 使用了 Unsafe 对象完成直接内存的分配回收,并且回收需要主动调用 freeMemory 方法

  • ByteBuffffer 的实现类内部,使用了 Cleaner (虚引用)来监测 ByteBuffffer 对象,一旦ByteBuffffer 对象被垃圾回收,那么就会由 ReferenceHandler 线程通过 Cleaner 的 clean 方法调

  • 用 freeMemory 来释放直接内存

ByteBuffer.allocateDirect(1024 * 1024)