• 【JVM】内存结构


    目录

    程序计数器

    栈内存溢出

    StringTable

    StringTable的特性

    StringTable所处位置

    StringTable垃圾回收机制

    StringTable调优


    学习路线

    程序计数器

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

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

    当一条指令执行时,会先送到解释器,解释器将指令转换为机器码,传递给CPU,然后程序计数器记录吓一跳指令的地址。

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

    Java Virtual Machine Stacks (Java 虚拟机栈)

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

    问题:

    1. 垃圾回收是否涉及栈内存?

    不涉及,因为在每次调用方法结束后,都会被弹出栈,会被自动的回收

    2. 栈内存分配越大越好吗?

    -xss给虚拟机划分内存,栈内存化的越大,反而会让线程数变少,因为物理内存是一定的,栈内存越大,线程数就变少了

    3. 方法内的局部变量是否线程安全?

    如果方法内局部变量没有逃离方法的作用访问,它是线程安全的

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

    内存溢出

    1. 栈帧过多导致栈内存溢出
    2. 栈帧过大导致栈内存溢出

    Heap 堆

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

    特点

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

    StringTable

    面试题;

    1. String s1 = "a";
    2. String s2 = "b";
    3. String s3 = "a" + "b";//"ab"
    4. String s4 = s1 + s2;//new String("ab")
    5. String s5 = "ab";
    6. String s6 = s4.intern();
    7. // 问
    8. System.out.println(s3 == s4);//false
    9. System.out.println(s3 == s5);//true
    10. System.out.println(s3 == s6);//true
    11. String x2 = new String("c") + new String("d");//new String ("cd")
    12. String x1 = "cd";
    13. x2.intern();
    14. // 问,如果调换了【最后两行代码】的位置呢,如果是jdk1.6呢
    15. System.out.println(x1 == x2);//false 因为常量池中已经有了"cd"所以x2.intern(); 不会将x2放入常量池
    16. ???J跟换[最后]两行代码
    17. String x2 = new String("c") + new String("d");//new String ("cd")
    18. x2.intern();
    19. String x1 = "cd";
    20. System.out.println(x1 == x2);//true 因为x2.intern()时,常量池没有"cd"所以放入了常量池
    21. ???如果在JDK1.6版本中呢
    22. String x2 = new String("c") + new String("d");//new String ("cd")
    23. x2.intern();
    24. String x1 = "cd";
    25. System.out.println(x1 == x2);//false 因为1.6中只会讲副本放入进去

    先看下面的这个代码

    1. public class demo01 {
    2. public static void main(String[] args) {
    3. String s1 = "a";
    4. String s2 = "b";
    5. String s3 = "ab";
    6. }
    7. }

    我们来分析一下这个代码

    使用jclasslib插件工具,查看字节码

    在编译下面的代码、

    1. public class demo01 {
    2. public static void main(String[] args) {
    3. String s1 = "a";
    4. String s2 = "b";
    5. String s3 = "ab";
    6. String s4 = s1 + s2;
    7. System.out.println(s3 == s4);
    8. }
    9. }

    查看了字节码的流程以后我们可以知道,实际上S4的创建经历了

    new StringBuilder.append("a").append("b").toString()

    我们再来查看一下StringBuilder的toString()方法

    1. public final class StringBuilder
    2. extends AbstractStringBuilder
    3. implements java.io.Serializable, CharSequence
    4. {
    5. @Override
    6. public String toString() {
    7. // Create a copy, don't share the array
    8. return new String(value, 0, count);
    9. }
    10. }

    tostring()方法里面创建了一个新的对像,所以我们可以确定s3不等于s4;

    创建出来的s3="ab"是存在与常量池中的,而s4则是存在于栈内存中

    我们再来查看下面的代码,判断s3==s5

    1. public class demo01 {
    2. public static void main(String[] args) {
    3. String s1 = "a";
    4. String s2 = "b";
    5. String s3 = "ab";
    6. String s4 = s1 + s2;
    7. System.out.println(s3 == s4);
    8. String s5 = "a" + "b";
    9. System.out.println(s3 == s5);
    10. }
    11. }

    System.out.println(s3 == s5); //true

    StringTable的特性

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

    池中的对象返回

      • 1.6 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有会把此对象复制一份, (注意本身不会放入串池,而是将复制的放入串池)放入串池, 会把串池中的对象返回

    JDK1.8特性

    1. public class demo01 {
    2. public static void main(String[] args) {
    3. String x = "ab";
    4. String s1 = new String ("a") + new String ("b");
    5. String s2 = s1.intern();
    6. System.out.println(x == s2); //true
    7. System.out.println(x == s1); //false
    8. }
    9. }

    JDK1.6特性

    StringTable所处位置

    在JDk1.6和JDK1.8时所处的位置

    StringTable垃圾回收机制

    当内存空间不足时,长时间没引用的字符串常量会被回收

    StringTable的底层是哈希表

    StringTable调优

    • 调整 -XX:StringTableSize=桶个数
    • 考虑将字符串对象是否入池

  • 相关阅读:
    全球首例:肾衰7年的他移植了一颗猪肾脏
    aardio + .NET 快速开发独立 EXE 程序,可防 ILSpy 反编译
    TikTok | 营销人必看 最接地气的TikTok视频营销策略到此
    python超细致带你批量获取美女内容~ 快来观赏叭
    OSPF,RIP和BGP的路由汇总
    Tomcat 9.0.41在IDEA中乱码问题(IntelliJ IDEA 2022.1.3版本)
    js异步任务的简单总结
    electron.js入门-为生产环境构建应用程序
    Vue中自定义指令用法
    插值问 题
  • 原文地址:https://blog.csdn.net/weixin_57597001/article/details/127599916