【JVM调优实战100例】04——方法区调优实战(中)

简介: 文章目录7.方法区7.1 定义7.2 方法区内存溢出7.3 常量池7.4 String table7.5 String table的位置7.6 String table的垃圾回收7.7 String table调优


在jvm启动时,常量池中的内容都会加载到运行时常量池中,但是此时a,b,ab都还只是一个符号,而不是字符串对象。只有当执行到具体的指令,如0: ldc #2才会创建字符串对象"a"。于此同时,jvm还会去String table[]中去找是否有"a"这个字符串,如果没有则将其加入String table[]。注:String table[]其实是hashtable 结构,不能扩容。


在java代码中新增s4,并反编译。

String s4 = s1 + s2; 

反编译结果如下。

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/StringBuilder;
20: aload_2
21: invokevirtual #7   // Method java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lang/StringBuilder;
24: invokevirtual #8   // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
27: astore        4


以上操作等同于。


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

其中toString()的方法实现方式是:new String("ab")。所以s3 == s4的结果为fasle.

System.out.println(s3 == s4); //false
1

接着我们在代码中新增s5.


String s5 = "a" + "b";
1

反编译结果如下。


29: ldc           #4                  // String ab
31: astore        5

原来,javac编译时帮助我们进行了优化, 它认为“a”,“b”是常量,结果不可能会发生改变,于是结果直接在编译期确定为ab了。并且,由于"ab"在String table中已经存在,因此不会创建新的字符串对象了。

System.out.println(s3 == s4);  //true
1

intern()方法可以把堆中的字符串对象放入串中,参考以下代码。

public class Demo1_23 {
    // String table["ab", "a", "b"]
    public static void main(String[] args) {
        String x = "ab"; 
        String s = new String("a") + new String("b");     // 堆  new String("a")   new String("b") new String("ab")
        String s2 = s.intern();//将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池,会把串池中的对象返回
        System.out.println( s2 == x);  //true,s2与x都是串池中的对象
        System.out.println( s == x ); //false,s是堆中的对象,与串池中的对象是不同的对象
    }
}


下面这种情况x2可以成功加入串池,因此结果为true。

String x2 = new String("c") + new String("d"); // new String("cd")
x2.intern();
String x1 = "cd";
System.out.println(x1 == x2);  //true

不过jdk1.6中调用intern()方法,会将字符串尝试放入串池,如果有则不会放入,如果没有则会复制一份放入串池,因此,串池中的对象与堆中的对象并不是同一个对象。上面同样的代码再jdk1.6中x1 == x2返回false。


串池的特点总结如下。

7.5 String table的位置

在jdk1.6,string table置于常量池,而常量池位于永久代的方法区中。永久代只有full gc触发时才会进行回收,这就导致string table的回收效率低。jdk1.7将string table移到了堆中。


7.6 String table的垃圾回收

参考以下代码配置参数并运行。

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

打印信息如下

[GC (Allocation Failure) [PSYoungGen: 2048K->488K(2560K)] 2048K->875K(9728K), 0.0028226 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 2536K->512K(2560K)] 2923K->958K(9728K), 0.0039494 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 2560K->512K(2560K)] 3006K->1006K(9728K), 0.0020900 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
...
StringTable statistics:
Number of buckets       :     60013 =    480104 bytes, avg   8.000
Number of entries       :     26231 =    629544 bytes, avg  24.000
Number of literals      :     26231 =   1548152 bytes, avg  59.020
Total footprint         :           =   2657800 bytes
Average bucket size     :     0.437
Variance of bucket size :     0.418
Std. dev. of bucket size:     0.646
Maximum bucket size     :         4
相关文章
|
2月前
|
算法 Java 关系型数据库
掌握这3个技巧,你也可以秒懂JAVA性能调优和jvm垃圾回收
JVM 是一个虚拟化的操作系统,类似于 Linux 和 Window,只是他被架构在了操作系统上进行接收 class 文件并把 class 翻译成系统识别的机器码进行执行,即 JVM 为我们屏蔽了不同操作系统在底层硬件和操作指令的不同。
24 0
|
27天前
|
缓存 Java C#
【JVM故障问题排查心得】「Java技术体系方向」Java虚拟机内存优化之虚拟机参数调优原理介绍(一)
【JVM故障问题排查心得】「Java技术体系方向」Java虚拟机内存优化之虚拟机参数调优原理介绍
75 0
|
3天前
|
存储 安全 Java
JVM之本地方法栈和程序计数器和堆
JVM之本地方法栈和程序计数器和堆
9 0
|
9天前
|
监控 前端开发 安全
JVM工作原理与实战(十四):JDK9及之后的类加载器
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了JDK8及之前的类加载器、JDK9及之后的类加载器等内容。
18 2
|
9天前
|
监控 Java 关系型数据库
JVM工作原理与实战(十三):打破双亲委派机制-线程上下文类加载器
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了打破双亲委派机制的方法、线程上下文类加载器等内容。
13 2
|
9天前
|
存储 XML 监控
JVM工作原理与实战(三):字节码文件的组成
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了字节码文件的基础信息、常量池、方法、字段、属性等内容。
26 6
|
1月前
|
存储 Java 编译器
【JVM】深入了解JVM方法区
【JVM】深入了解JVM方法区
20 0
|
1月前
|
算法 Java
JVM中判断对象是否需要回收的方法
JVM中判断对象是否需要回收的方法
|
2月前
|
存储 算法 Java
工作5年,我竟发现JVM只用这4个技巧就可以轻松调优
Java虚拟机中,数据类型可以分为两类:基本类型和引用类型。基本类型的变量保存原始值,即:他代表的值就是数值本身;而引用类型的变量保存引用值。“引用值”代表了某个对象的引用,而不是对象本身,对象本身存放在这个引用值所表示的地址的位置。
10 0
|
2月前
|
Java