JVM性能优化------内存结构原理分析(二)

项目地址
jvm_03

前面介绍了jvm内存结构,现在我们来介绍一下常量池。我们都知道常量的对的定义。常量是一个固定的值,不会发生改变。比如:配置文件,数据库连接池基本配置等等。

常量池
常量的定义在上面提到过了,那我们来了解一下常量池,简单的看就是存储常量的地方。

这是百度百科的解释:

常量池在java用于保存在编译期已确定的,已编译的class文件中的一份数据。它包括了关于类,
方法,接口等中的常量,也包括字符串常量,如String s = "java"这种申明方式;当然也可扩
充,执行器产生的常量也会放入常量池,故认为常量池是JVM的一块特殊的内存空间。

看到这里我们也应该知道了,常量池是处于方法区。但是事实不是全部的常量池都在方法区。常量池分为

  1. class常量池(静态常量池)
    核心概念:定义字面量(自己定义的变量)、符号引用(类的全限定名–>包名+类名)
  2. 运行常量池
    classLoad将静态常量池中会被需要的常量放入运行常量池
  3. 字符串常量池
    在jdk1.6和以前放在方法区(永久区);
    在jdk1.7常量池放入到堆中
    在jdk1.8时放在堆内存中的字符串常量池,其他的常量都是放在元空间

为什么一个字符串常量池的位置入池该来该去?最后改到jdk1.8的情况。主要是为了方便GC回收。
那为什么要常量池放到堆中,因为如果放在方法区,GC不会经常的回收,所以耗费内存。那为什么jdk1.7把常量池放到了堆中,还不满足呢?因为常量池中不只有局部变量,也有全局变量,总不可能吧全局变量提前给回收了吧? 所以最后只把字符串常量池放到堆中。主要就是为了防止全局常量也会在堆中被回收。

我们在看一些文章的时候,总会说new String、String、String+String。的区别。博主使用以下代码来说。

/**
 * @author 龙小虬
 * @date 2021/4/13 14:35
 */
public class Main {
    public static void main(String[] args) {
        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);
        System.out.println(s3 == s5);
        System.out.println(s3 == s6);
    }
}

这段代码的结果是什么呢?片面的说什么对象、常量很难记住。我们直接看汇编代码吧。(javap -c -v C:\Users\HP\IdeaProjects\jvm_03\target\classes\Main.class
在这里插入图片描述
之别反汇编。
可以看到结果。

public class Main
  minor version: 0
  major version: 49
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #13.#34        // java/lang/Object."<init>":()V
   #2 = String             #35            // a
   #3 = String             #36            // b
   #4 = String             #37            // ab
   #5 = Class              #38            // java/lang/StringBuilder
   #6 = Methodref          #5.#34         // java/lang/StringBuilder."<init>":()V
   #7 = Methodref          #5.#39         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   #8 = Methodref          #5.#40         // java/lang/StringBuilder.toString:()Ljava/lang/String;
   #9 = Methodref          #41.#42        // java/lang/String.intern:()Ljava/lang/String;
  #10 = Fieldref           #43.#44        // java/lang/System.out:Ljava/io/PrintStream;
  #11 = Methodref          #45.#46        // java/io/PrintStream.println:(Z)V
  #12 = Class              #47            // Main
  #13 = Class              #48            // java/lang/Object
  #14 = Utf8               <init>
  #15 = Utf8               ()V
  #16 = Utf8               Code
  #17 = Utf8               LineNumberTable
  #18 = Utf8               LocalVariableTable
  #19 = Utf8               this
  #20 = Utf8               LMain;
  #21 = Utf8               main
  #22 = Utf8               ([Ljava/lang/String;)V
  #23 = Utf8               args
  #24 = Utf8               [Ljava/lang/String;
  #25 = Utf8               s1
  #26 = Utf8               Ljava/lang/String;
  #27 = Utf8               s2
  #28 = Utf8               s3
  #29 = Utf8               s4
  #30 = Utf8               s5
  #31 = Utf8               s6
  #32 = Utf8               SourceFile
  #33 = Utf8               Main.java
  #34 = NameAndType        #14:#15        // "<init>":()V
  #35 = Utf8               a
  #36 = Utf8               b
  #37 = Utf8               ab
  #38 = Utf8               java/lang/StringBuilder
  #39 = NameAndType        #49:#50        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #40 = NameAndType        #51:#52        // toString:()Ljava/lang/String;
  #41 = Class              #53            // java/lang/String
  #42 = NameAndType        #54:#52        // intern:()Ljava/lang/String;
  #43 = Class              #55            // java/lang/System
  #44 = NameAndType        #56:#57        // out:Ljava/io/PrintStream;
  #45 = Class              #58            // java/io/PrintStream
  #46 = NameAndType        #59:#60        // println:(Z)V
  #47 = Utf8               Main
  #48 = Utf8               java/lang/Object
  #49 = Utf8               append
  #50 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #51 = Utf8               toString
  #52 = Utf8               ()Ljava/lang/String;
  #53 = Utf8               java/lang/String
  #54 = Utf8               intern
  #55 = Utf8               java/lang/System
  #56 = Utf8               out
  #57 = Utf8               Ljava/io/PrintStream;
  #58 = Utf8               java/io/PrintStream
  #59 = Utf8               println
  #60 = Utf8               (Z)V
{
  public Main();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 5: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LMain;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=7, args_size=1
         0: ldc           #2                  // String a
         2: astore_1
         3: ldc           #3                  // String b
         5: astore_2
         6: ldc           #4                  // String ab
         8: astore_3
         9: new           #5                  // class java/lang/StringBuilder
        12: dup
        13: invokespecial #6                  // Method java/lang/StringBuilder."<init>":()V
        16: aload_1
        17: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/Strin
gBuilder;
        20: aload_2
        21: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/Strin
gBuilder;
        24: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        27: astore        4
        29: ldc           #4                  // String ab
        31: astore        5
        33: aload         4
        35: invokevirtual #9                  // Method java/lang/String.intern:()Ljava/lang/String;
        38: astore        6
        40: getstatic     #10                 // Field java/lang/System.out:Ljava/io/PrintStream;
        43: aload_3
        44: aload         4
        46: if_acmpne     53
        49: iconst_1
        50: goto          54
        53: iconst_0
        54: invokevirtual #11                 // Method java/io/PrintStream.println:(Z)V
        57: getstatic     #10                 // Field java/lang/System.out:Ljava/io/PrintStream;
        60: aload_3
        61: aload         5
        63: if_acmpne     70
        66: iconst_1
        67: goto          71
        70: iconst_0
        71: invokevirtual #11                 // Method java/io/PrintStream.println:(Z)V
        74: getstatic     #10                 // Field java/lang/System.out:Ljava/io/PrintStream;
        77: aload_3
        78: aload         6
        80: if_acmpne     87
        83: iconst_1
        84: goto          88
        87: iconst_0
        88: invokevirtual #11                 // Method java/io/PrintStream.println:(Z)V
        91: return
      LineNumberTable:
        line 7: 0
        line 8: 3
        line 9: 6
        line 10: 9
        line 11: 29
        line 12: 33
        line 13: 40
        line 14: 57
        line 15: 74
        line 16: 91
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      92     0  args   [Ljava/lang/String;
            3      89     1    s1   Ljava/lang/String;
            6      86     2    s2   Ljava/lang/String;
            9      83     3    s3   Ljava/lang/String;
           29      63     4    s4   Ljava/lang/String;
           33      59     5    s5   Ljava/lang/String;
           40      52     6    s6   Ljava/lang/String;
}

这样看的话,代码量有点大。我们分开理解吧。

/**
 * @author 龙小虬
 * @date 2021/4/13 14:35
 */
public class Main {
    public static void main(String[] args) {
        System.out.println("lxq");
    }
}

先反汇编此段代码。

public class Main
  minor version: 0
  major version: 49
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #6.#20         // java/lang/Object."<init>":()V
   #2 = Fieldref           #21.#22        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #23            // lxq
   #4 = Methodref          #24.#25        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = Class              #26            // Main
   #6 = Class              #27            // java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               LocalVariableTable
  #12 = Utf8               this
  #13 = Utf8               LMain;
  #14 = Utf8               main
  #15 = Utf8               ([Ljava/lang/String;)V
  #16 = Utf8               args
  #17 = Utf8               [Ljava/lang/String;
  #18 = Utf8               SourceFile
  #19 = Utf8               Main.java
  #20 = NameAndType        #7:#8          // "<init>":()V
  #21 = Class              #28            // java/lang/System
  #22 = NameAndType        #29:#30        // out:Ljava/io/PrintStream;
  #23 = Utf8               lxq
  #24 = Class              #31            // java/io/PrintStream
  #25 = NameAndType        #32:#33        // println:(Ljava/lang/String;)V
  #26 = Utf8               Main
  #27 = Utf8               java/lang/Object
  #28 = Utf8               java/lang/System
  #29 = Utf8               out
  #30 = Utf8               Ljava/io/PrintStream;
  #31 = Utf8               java/io/PrintStream
  #32 = Utf8               println
  #33 = Utf8               (Ljava/lang/String;)V
{
  public Main();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 5: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LMain;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String lxq
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 7: 0
        line 8: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  args   [Ljava/lang/String;
}

Constant pool:
这里看一眼应该都知道是什么吧?------常量池
那我们先找到我们的main()方法。
public static void main(java.lang.String[]);
应该很好找到的,就是这里,我们的main()方法。
在这里插入图片描述
这里就是我们的main()方法中的汇编代码
那么怎么看呢?
我们可以看到其中有#2、#3、#4这就是我们前面提到的常量池里面前面那一部分吧?那前面的0、3、5、8是什么呢?这个就是我们的栈帧。废话不多说。直接看怎么去查询。找到#2
在这里插入图片描述

这里要我们去查找#22 #23
在这里插入图片描述
同理。
在这里插入图片描述
这里可以看到了这是我们java输出代码前面的一部分System.out
现在就拿到了System.out,再去看#3
在这里插入图片描述
在这里插入图片描述
这里拿到了我们的字符串常量。
在看到#4
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
获取到println,然后将字符串输出。
基本的查找应该都了解了。那么我们就来看看。

/**
 * @author 龙小虬
 * @date 2021/4/13 14:35
 */
public class Main {
    public static void main(String[] args) {
        String a1 = "a";
        String b1 = "b";
        String ab1 = "ab";
        String ab2 = a1 + b1;
        System.out.println(ab1 == ab2);
    }
}

那么他们会相等吗?
我们先看汇编代码。

public class Main
  minor version: 0
  major version: 49
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #12.#31        // java/lang/Object."<init>":()V
   #2 = String             #32            // a
   #3 = String             #33            // b
   #4 = String             #34            // ab
   #5 = Class              #35            // java/lang/StringBuilder
   #6 = Methodref          #5.#31         // java/lang/StringBuilder."<init>":()V
   #7 = Methodref          #5.#36         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   #8 = Methodref          #5.#37         // java/lang/StringBuilder.toString:()Ljava/lang/String;
   #9 = Fieldref           #38.#39        // java/lang/System.out:Ljava/io/PrintStream;
  #10 = Methodref          #40.#41        // java/io/PrintStream.println:(Z)V
  #11 = Class              #42            // Main
  #12 = Class              #43            // java/lang/Object
  #13 = Utf8               <init>
  #14 = Utf8               ()V
  #15 = Utf8               Code
  #16 = Utf8               LineNumberTable
  #17 = Utf8               LocalVariableTable
  #18 = Utf8               this
  #19 = Utf8               LMain;
  #20 = Utf8               main
  #21 = Utf8               ([Ljava/lang/String;)V
  #22 = Utf8               args
  #23 = Utf8               [Ljava/lang/String;
  #24 = Utf8               a1
  #25 = Utf8               Ljava/lang/String;
  #26 = Utf8               b1
  #27 = Utf8               ab1
  #28 = Utf8               ab2
  #29 = Utf8               SourceFile
  #30 = Utf8               Main.java
  #31 = NameAndType        #13:#14        // "<init>":()V
  #32 = Utf8               a
  #33 = Utf8               b
  #34 = Utf8               ab
  #35 = Utf8               java/lang/StringBuilder
  #36 = NameAndType        #44:#45        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #37 = NameAndType        #46:#47        // toString:()Ljava/lang/String;
  #38 = Class              #48            // java/lang/System
  #39 = NameAndType        #49:#50        // out:Ljava/io/PrintStream;
  #40 = Class              #51            // java/io/PrintStream
  #41 = NameAndType        #52:#53        // println:(Z)V
  #42 = Utf8               Main
  #43 = Utf8               java/lang/Object
  #44 = Utf8               append
  #45 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #46 = Utf8               toString
  #47 = Utf8               ()Ljava/lang/String;
  #48 = Utf8               java/lang/System
  #49 = Utf8               out
  #50 = Utf8               Ljava/io/PrintStream;
  #51 = Utf8               java/io/PrintStream
  #52 = Utf8               println
  #53 = Utf8               (Z)V
{
  public Main();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 5: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LMain;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=5, args_size=1
         0: ldc           #2                  // String a
         2: astore_1
         3: ldc           #3                  // String b
         5: astore_2
         6: ldc           #4                  // String ab
         8: astore_3
         9: new           #5                  // class java/lang/StringBuilder
        12: dup
        13: invokespecial #6                  // Method java/lang/StringBuilder."<init>":()V
        16: aload_1
        17: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/Strin
gBuilder;
        20: aload_2
        21: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/Strin
gBuilder;
        24: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        27: astore        4
        29: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
        32: aload_3
        33: aload         4
        35: if_acmpne     42
        38: iconst_1
        39: goto          43
        42: iconst_0
        43: invokevirtual #10                 // Method java/io/PrintStream.println:(Z)V
        46: return
      LineNumberTable:
        line 7: 0
        line 8: 3
        line 9: 6
        line 10: 9
        line 11: 29
        line 12: 46
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      47     0  args   [Ljava/lang/String;
            3      44     1    a1   Ljava/lang/String;
            6      41     2    b1   Ljava/lang/String;
            9      38     3   ab1   Ljava/lang/String;
           29      18     4   ab2   Ljava/lang/String;
}

傻了,还是很多,但是我们了解了怎么去看,这就很简单了。
照常看到
在这里插入图片描述
一行一行查看。
#2
在这里插入图片描述
在这里插入图片描述
#3
在这里插入图片描述
在这里插入图片描述
#4
在这里插入图片描述
在这里插入图片描述

这里就是三个常量的地址。
而astore_1、 astore_2、 astore_3就得查询汇编指令了。
在这里插入图片描述
在这里插入图片描述
诶???这里怎么看到一个new ?那我们继续去找找为什么。
在这里插入图片描述
在这里插入图片描述
StringBuild?我们没有使用他啊。
再往下看
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
#13init,#14()。这应该就是代表无参构造吧?
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这就是调用刚刚创建的StringBuild进行append()吧?将两个常量进行append()
在这里插入图片描述
if_acmpne有条件转移。那也就是转移到42咯,
#8
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这也就让我们知道了a1 + b1;其实就是new StringBuild()之后进行了append(a1)和append(b1),之后再进行toString()。

StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(a1);
stringBuilder.append(b1);
stringBuilder.toString();

那他这里已经进行了toString(),那么我们应该拿到的是值啊,为什么会不相等?我们先看看,这个toString()方法。
在这里插入图片描述
原来他返回的是一个对象,返回的时候直接new String()了。而在前面的文章提到过,进行new 的对象,是放在堆内存中的,并非字符串常量池,所以他们根本不会相等(因为“==”比较的是地址)
所以输出一定是false。
在这里插入图片描述
我们也可以看到运行结果也是false。
既然这个可以看懂了,那么我们再来看一个。

/**
 * @author 龙小虬
 * @date 2021/4/13 14:35
 */
public class Main {
    public static void main(String[] args) {
        String ab1 = "ab";
        String ab2 = "a" + "b";
        System.out.println(ab1 == ab2);
    }
}

变成汇编代码:

public class Main
  minor version: 0
  major version: 49
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #6.#23         // java/lang/Object."<init>":()V
   #2 = String             #24            // ab
   #3 = Fieldref           #25.#26        // java/lang/System.out:Ljava/io/PrintStream;
   #4 = Methodref          #27.#28        // java/io/PrintStream.println:(Z)V
   #5 = Class              #29            // Main
   #6 = Class              #30            // java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               LocalVariableTable
  #12 = Utf8               this
  #13 = Utf8               LMain;
  #14 = Utf8               main
  #15 = Utf8               ([Ljava/lang/String;)V
  #16 = Utf8               args
  #17 = Utf8               [Ljava/lang/String;
  #18 = Utf8               ab1
  #19 = Utf8               Ljava/lang/String;
  #20 = Utf8               ab2
  #21 = Utf8               SourceFile
  #22 = Utf8               Main.java
  #23 = NameAndType        #7:#8          // "<init>":()V
  #24 = Utf8               ab
  #25 = Class              #31            // java/lang/System
  #26 = NameAndType        #32:#33        // out:Ljava/io/PrintStream;
  #27 = Class              #34            // java/io/PrintStream
  #28 = NameAndType        #35:#36        // println:(Z)V
  #29 = Utf8               Main
  #30 = Utf8               java/lang/Object
  #31 = Utf8               java/lang/System
  #32 = Utf8               out
  #33 = Utf8               Ljava/io/PrintStream;
  #34 = Utf8               java/io/PrintStream
  #35 = Utf8               println
  #36 = Utf8               (Z)V
{
  public Main();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 5: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LMain;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=3, args_size=1
         0: ldc           #2                  // String ab
         2: astore_1
         3: ldc           #2                  // String ab
         5: astore_2
         6: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
         9: aload_1
        10: aload_2
        11: if_acmpne     18
        14: iconst_1
        15: goto          19
        18: iconst_0
        19: invokevirtual #4                  // Method java/io/PrintStream.println:(Z)V
        22: return
      LineNumberTable:
        line 7: 0
        line 8: 3
        line 9: 6
        line 10: 22
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      23     0  args   [Ljava/lang/String;
            3      20     1   ab1   Ljava/lang/String;
            6      17     2   ab2   Ljava/lang/String;
}

看到
在这里插入图片描述
可以发现他们寻找的地址都是一样的,按照上面的寻找方法,那肯定就是两个地址一模一样的
在这里插入图片描述
返回的肯定为true。
在这里插入图片描述
这个我们懂了之后,再来看一个方法。

intern()
这个方法很奇怪,他可以将我们对象放入到字符串常量池中,如果常量池中存在 则不会放入。我们来验证一下吧。

/**
 * @author 龙小虬
 * @date 2021/4/13 14:35
 */
public class Main {
    public static void main(String[] args) {
        String a1 = "a";
        String b1 = "b";
        String ab = a1 + b1;
        String ab2 = "ab";
        System.out.println(ab == ab2);
        String ab1 = ab.intern();
        System.out.println(ab1 == ab2);
    }
}

汇编代码:

public class Main
  minor version: 0
  major version: 49
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #13.#33        // java/lang/Object."<init>":()V
   #2 = String             #34            // a
   #3 = String             #35            // b
   #4 = Class              #36            // java/lang/StringBuilder
   #5 = Methodref          #4.#33         // java/lang/StringBuilder."<init>":()V
   #6 = Methodref          #4.#37         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   #7 = Methodref          #4.#38         // java/lang/StringBuilder.toString:()Ljava/lang/String;
   #8 = String             #28            // ab
   #9 = Fieldref           #39.#40        // java/lang/System.out:Ljava/io/PrintStream;
  #10 = Methodref          #41.#42        // java/io/PrintStream.println:(Z)V
  #11 = Methodref          #43.#44        // java/lang/String.intern:()Ljava/lang/String;
  #12 = Class              #45            // Main
  #13 = Class              #46            // java/lang/Object
  #14 = Utf8               <init>
  #15 = Utf8               ()V
  #16 = Utf8               Code
  #17 = Utf8               LineNumberTable
  #18 = Utf8               LocalVariableTable
  #19 = Utf8               this
  #20 = Utf8               LMain;
  #21 = Utf8               main
  #22 = Utf8               ([Ljava/lang/String;)V
  #23 = Utf8               args
  #24 = Utf8               [Ljava/lang/String;
  #25 = Utf8               a1
  #26 = Utf8               Ljava/lang/String;
  #27 = Utf8               b1
  #28 = Utf8               ab
  #29 = Utf8               ab2
  #30 = Utf8               ab1
  #31 = Utf8               SourceFile
  #32 = Utf8               Main.java
  #33 = NameAndType        #14:#15        // "<init>":()V
  #34 = Utf8               a
  #35 = Utf8               b
  #36 = Utf8               java/lang/StringBuilder
  #37 = NameAndType        #47:#48        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #38 = NameAndType        #49:#50        // toString:()Ljava/lang/String;
  #39 = Class              #51            // java/lang/System
  #40 = NameAndType        #52:#53        // out:Ljava/io/PrintStream;
  #41 = Class              #54            // java/io/PrintStream
  #42 = NameAndType        #55:#56        // println:(Z)V
  #43 = Class              #57            // java/lang/String
  #44 = NameAndType        #58:#50        // intern:()Ljava/lang/String;
  #45 = Utf8               Main
  #46 = Utf8               java/lang/Object
  #47 = Utf8               append
  #48 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #49 = Utf8               toString
  #50 = Utf8               ()Ljava/lang/String;
  #51 = Utf8               java/lang/System
  #52 = Utf8               out
  #53 = Utf8               Ljava/io/PrintStream;
  #54 = Utf8               java/io/PrintStream
  #55 = Utf8               println
  #56 = Utf8               (Z)V
  #57 = Utf8               java/lang/String
  #58 = Utf8               intern
{
  public Main();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 5: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LMain;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=6, args_size=1
         0: ldc           #2                  // String a
         2: astore_1
         3: ldc           #3                  // String b
         5: astore_2
         6: new           #4                  // class java/lang/StringBuilder
         9: dup
        10: invokespecial #5                  // Method java/lang/StringBuilder."<init>":()V
        13: aload_1
        14: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/Strin
gBuilder;
        17: aload_2
        18: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/Strin
gBuilder;
        21: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        24: astore_3
        25: ldc           #8                  // String ab
        27: astore        4
        29: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
        32: aload_3
        33: aload         4
        35: if_acmpne     42
        38: iconst_1
        39: goto          43
        42: iconst_0
        43: invokevirtual #10                 // Method java/io/PrintStream.println:(Z)V
        46: aload_3
        47: invokevirtual #11                 // Method java/lang/String.intern:()Ljava/lang/String;
        50: astore        5
        52: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
        55: aload         5
        57: aload         4
        59: if_acmpne     66
        62: iconst_1
        63: goto          67
        66: iconst_0
        67: invokevirtual #10                 // Method java/io/PrintStream.println:(Z)V
        70: return
      LineNumberTable:
        line 7: 0
        line 8: 3
        line 9: 6
        line 10: 25
        line 11: 29
        line 12: 46
        line 13: 52
        line 14: 70
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      71     0  args   [Ljava/lang/String;
            3      68     1    a1   Ljava/lang/String;
            6      65     2    b1   Ljava/lang/String;
           25      46     3    ab   Ljava/lang/String;
           29      42     4   ab2   Ljava/lang/String;
           52      19     5   ab1   Ljava/lang/String;
}

我们主要看到这个,其他的都很好理解
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
将new String()又进行转换,变成了字符串,赋值给ab1。所以他们肯定是相等的。毕竟都在字符串常量池了。
运行结果:
在这里插入图片描述

下面我们了解了常量池,和String字符串的比较,那就再来看看内存溢出和内存泄露吧。
内存溢出
概念:在申请内存之前,内存不够需求,供小于求

  1. 堆内存溢出
    arraylist存数据
    代码示例:
import java.util.ArrayList;

/**
 * @author 龙小虬
 * @date 2021/4/13 14:35
 * -Xmx8m
 */
public class HeapTest {
    public static void main(String[] args) {
        int i = 0;
        try {
            ArrayList<String> strings = new ArrayList<String>();
            while (true) {
                strings.add("mayikt");
                i++;
            }

        } catch (Exception e) {

        }
    }
}

在这里插入图片描述

  1. 方法区
    定义常量过多(一般发生在项目启动)
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.Opcodes;

/**
 * @author 龙小虬
 * @date 2021/4/13 22:40
 * -XX:MaxMetaspaceSize=8m -XX:-UseCompressedClassPointers
 */
public class MetaspaceTest extends ClassLoader {
    public static void main(String[] args) {
        int j = 0;
        try {
            MetaspaceTest test = new MetaspaceTest();
            for (int i = 0; i < 30000; i++, j++) {
                ClassWriter cw = new ClassWriter(0);
                cw.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC, "Class" + i, null, "java/lang/Object", null);
                byte[] code = cw.toByteArray();
                test.defineClass("Class" + i, code, 0, code.length);
            }
        } finally {
            System.out.println(j);
        }
    }
}

在这里插入图片描述

  1. 本地方法栈
/**
 * @author 龙小虬
 * @date 2021/4/13 22:45
 * -Xss256k
 */
public class StackTest {
    private static int count;

    public static void main(String[] args) {
        lxq();
    }

    private static void lxq() {
        count++;
        System.out.println("count:" + count);
        lxq();
    }
}

在这里插入图片描述

内存泄露
概念:在申请内存之后,一直无法被GC回收,有可能会发生内存溢出问题(发生在程序运行一段时间)。
比如:HashMap将自定义对象为key

import java.util.HashMap;

/**
 * @author 龙小虬
 * @date 2021/4/13 22:47
 * -Xmx3M -Xms3M
 */
public class HashMapMemoryLeak {
    public static void main(String[] args) {

        HashMap<HashKey, Integer> map = new HashMap<HashKey, Integer>(1000);
        int counter = 0;
        while (true) {
            //循环插入新对象 new出很多很多内存地址不等的对象但是
            HashKey p = new HashKey("lxq", "lxq666");
            map.put(p, 1);
            counter++;
            if (counter % 1000 == 0) {
                System.out.println("map size: " + map.size());
                System.out.println("运行" + counter
                        + "次后,可用内存剩余" + Runtime.getRuntime().freeMemory() / (1024 * 1024) + "MB");
            }
        }
    }
}

class HashKey {
    private final String id;
    private String name;

    public HashKey(String name, String id) {
        this.name = name;
        this.id = id;
    }

    public void setName(String name) {
        this.name = name;
    }

//    @Override
//    public int hashCode() {
//        return id.hashCode();
//    }
//
//    @Override
//    public boolean equals(Object obj) {
//        if (obj instanceof HashKey)
//            return name.equals(((HashKey) obj).name) && id.equals(((HashKey) obj).id);
//        else
//            return false;
//    }
}

在这里插入图片描述
没有将自定义的对象没有重写hashcode()、equals()方法。所以会在运行过程中发生内存溢出,局部变量p一直无法回收。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龙小虬

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值