精华内容
下载资源
问答
  • 1、常量 常量在java中就值的是一般的字面量,比如字符串,整数,浮点数等等数据。简单理解java中什么叫常量 2、常量池,也叫静态常量池,说常量池一定要指明是编译器生产

    1、常量
    常量在java中就值的是一般的字面量,比如字符串,整数,浮点数等等数据。简单理解java中什么叫常量

    2、常量池,也叫静态常量池或者class文件常量池,说常量池一定要指明是编译器产生的。它的组成为字面量和符号引用
    在这里插入图片描述
    3、运行时常量池。当类加载到内存中后,jvm就会将class常量池中的内容存放到运行时常量池中,由此可知,运行时常量池也是每个类都有一个。运行时常量池在jdk1.8时,在方法区(即元空间)中。

    4、字符串常量池。就是String类型的字符串,包括代码写的字符串,比如方法名,类名都是字面量,还有String定义的字符串。字符串常量池,jdk1.8时在堆中,全局共享,独一份,之前在方法区中。

    那么问题就来了,
    1、字符串常量池在1.8时在堆内存中,如何证明。
    2、字符串常量能被GC回收吗?
    3、如何证明jdk1.8方法区为元空间。参考简单理解jdk1.8中的方法区


    上面1和2问题,通过一个实例可以证明,
    先设置JVM虚拟机启动参数-Xms1m -Xmx1m -XX:+PrintGCDetails -XX:MaxMetaspaceSize=8m。把堆内存设置小一点,1M。

    public class DemoClass {
        public static void main(String[] args) throws Exception {
            //字符串常量池在哪,在堆中。并且常量也会被GC回收。
            String s = "love";
            for (int i = 0; i < 10000000; i++) {
                s = s + "qsm-qsm-qsm-qsm-qsm-qsm-qsm-qsm-qsm-qsm-qsm-qsm-qsm-qsm-qsm-qsm-qsm-qsm-qsm-qsm-qsm-qsm-qsm-qsm-qsm-qsm-qsm-qsm-qsm";
                if (i%10==0){
                    System.out.println("第"+i+"次");
                    Thread.sleep(100);
                }
            }
        }
    }
    

    运行的结果截取一部分,如下

    [GC (Allocation Failure) [PSYoungGen: 512K->488K(1024K)] 512K->496K(1536K), 0.0010906 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
    Connected to the target VM, address: '127.0.0.1:51561', transport: 'socket'
    [GC (Allocation Failure) [PSYoungGen: 1000K->504K(1024K)] 1008K->632K(1536K), 0.0045804 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
    [GC (Allocation Failure) [PSYoungGen: 1002K->506K(1024K)] 1130K->786K(1536K), 0.0192090 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] 
    [GC (Allocation Failure) [PSYoungGen: 1013K->488K(1024K)] 1294K->896K(1536K), 0.0077057 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
    [Full GC (Ergonomics) [PSYoungGen: 488K->463K(1024K)] [ParOldGen: 408K->358K(512K)] 896K->822K(1536K), [Metaspace: 2930K->2930K(1056768K)], 0.0062971 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
    [Full GC (Ergonomics) [PSYoungGen: 974K->442K(1024K)] [ParOldGen: 358K->343K(512K)] 1332K->785K(1536K), [Metaspace: 3141K->3141K(1056768K)], 0.0066005 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
    第0次
    [Full GC (Ergonomics) [PSYoungGen: 954K->429K(1024K)] [ParOldGen: 343K->363K(512K)] 1297K->793K(1536K), [Metaspace: 3399K->3399K(1056768K)], 0.0065477 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
    第10次
    ---
    ---
    [Full GC (Ergonomics) [PSYoungGen: 603K->599K(1024K)] [ParOldGen: 480K->480K(512K)] 1084K->1080K(1536K), [Metaspace: 3405K->3405K(1056768K)], 0.0065548 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
    [Full GC (Ergonomics) [PSYoungGen: 940K->769K(1024K)] [ParOldGen: 480K->480K(512K)] 1421K->1250K(1536K), [Metaspace: 3405K->3405K(1056768K)], 0.0064790 secs] [Times: user=0.06 sys=0.00, real=0.01 secs] 
    [Full GC (Ergonomics) [PSYoungGen: 940K->428K(1024K)] [ParOldGen: 480K->481K(512K)] 1421K->909K(1536K), [Metaspace: 3405K->3405K(1056768K)], 0.0071489 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
    [Full GC (Ergonomics) [PSYoungGen: 604K->599K(1024K)] [ParOldGen: 481K->481K(512K)] 1085K->1080K(1536K), [Metaspace: 3405K->3405K(1056768K)], 0.0113774 secs] [Times: user=0.05 sys=0.00, real=0.01 secs] 
    [Full GC (Allocation Failure) [PSYoungGen: 599K->599K(1024K)] [ParOldGen: 481K->481K(512K)] 1080K->1080K(1536K), [Metaspace: 3405K->3405K(1056768K)], 0.0066770 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
    Heap
     PSYoungGen      total 1024K, used 649K [0x00000000ffe80000, 0x0000000100000000, 0x0000000100000000)
      eden space 512K, 43% used [0x00000000ffe80000,0x00000000ffeb71a8,0x00000000fff00000)
      from space 512K, 83% used [0x00000000fff80000,0x00000000fffeb2f8,0x0000000100000000)
      to   space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
     ParOldGen       total 512K, used 481K [0x00000000ffe00000, 0x00000000ffe80000, 0x00000000ffe80000)
      object space 512K, 93% used [0x00000000ffe00000,0x00000000ffe78488,0x00000000ffe80000)
     Metaspace       used 3436K, capacity 4556K, committed 4864K, reserved 1056768K
      class space    used 370K, capacity 392K, committed 512K, reserved 1048576K
    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    	at java.util.Arrays.copyOf(Arrays.java:3332)
    	at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)
    	at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:448)
    	at java.lang.StringBuilder.append(StringBuilder.java:136)
    	at com.jd.qsm.first.demo.controller.DemoClass.main(DemoClass.java:8)
    Disconnected from the target VM, address: '127.0.0.1:51561', transport: 'socket'
    

    回答第一个问题,由于我们一直生成的是字符串,最终导致OOM的原因是Java heap space堆空间,而在生成字符串时Metaspace几乎就没有什么变化过。所以证明字符串常量池在堆空间中。

    回答第二个问题,可以看到程序经历了很多次的GC和fullGC,看到新生代和老年代的内存占用都有减少,最终由于堆内存不足才OOM的。所以由于每次GC都有减少,所以字符串是可以被GC回收的。


    4、常量池怎么查看。
    5、常量池有什么作用,为什么需要它
    6、常量池与运行时常量池有什么关联

    回答4-6问题
    编写一段代码

    public class DemoClass {
        public static void main(String[] args) throws Exception {
            System.out.println("qsm");
        }
    }
    

    找到字节码文件,运行javap -v DemoClass.class

    D:\idea\code\firstdemo\target\classes\com\jd\qsm\first\demo\controller>javap -v DemoClass.class
    Classfile /D:/idea/code/firstdemo/target/classes/com/jd/qsm/first/demo/controller/DemoClass.class
      Last modified 2021-1-9; size 666 bytes
      MD5 checksum 711ba65168cdd7d23241623fe88153f6
      Compiled from "DemoClass.java"
    public class com.jd.qsm.first.demo.controller.DemoClass
      minor version: 0
      major version: 52
      flags: ACC_PUBLIC, ACC_SUPER
    Constant pool:
       #1 = Methodref          #6.#23         // java/lang/Object."<init>":()V
       #2 = Fieldref           #24.#25        // java/lang/System.out:Ljava/io/PrintStream;
       #3 = String             #26            // qsm
       #4 = Methodref          #27.#28        // java/io/PrintStream.println:(Ljava/lang/String;)V
       #5 = Class              #29            // com/jd/qsm/first/demo/controller/DemoClass
       #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               Lcom/jd/qsm/first/demo/controller/DemoClass;
      #14 = Utf8               main
      #15 = Utf8               ([Ljava/lang/String;)V
      #16 = Utf8               args
      #17 = Utf8               [Ljava/lang/String;
      #18 = Utf8               Exceptions
      #19 = Class              #31            // java/lang/Exception
      #20 = Utf8               MethodParameters
      #21 = Utf8               SourceFile
      #22 = Utf8               DemoClass.java
      #23 = NameAndType        #7:#8          // "<init>":()V
      #24 = Class              #32            // java/lang/System
      #25 = NameAndType        #33:#34        // out:Ljava/io/PrintStream;
      #26 = Utf8               qsm
      #27 = Class              #35            // java/io/PrintStream
      #28 = NameAndType        #36:#37        // println:(Ljava/lang/String;)V
      #29 = Utf8               com/jd/qsm/first/demo/controller/DemoClass
      #30 = Utf8               java/lang/Object
      #31 = Utf8               java/lang/Exception
      #32 = Utf8               java/lang/System
      #33 = Utf8               out
      #34 = Utf8               Ljava/io/PrintStream;
      #35 = Utf8               java/io/PrintStream
      #36 = Utf8               println
      #37 = Utf8               (Ljava/lang/String;)V
    {
      public com.jd.qsm.first.demo.controller.DemoClass();
        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 3: 0
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       5     0  this   Lcom/jd/qsm/first/demo/controller/DemoClass;
    
      public static void main(java.lang.String[]) throws java.lang.Exception;
        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 qsm
             5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
             8: return
          LineNumberTable:
            line 5: 0
            line 6: 8
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       9     0  args   [Ljava/lang/String;
        Exceptions:
          throws java.lang.Exception
        MethodParameters:
          Name                           Flags
          args
    }
    SourceFile: "DemoClass.java"
    
    

    从的出来的结果可以看到有一部分叫做Constant pool:,这里就是class文件常量池,是编译之后生成的,是一个静态的概念。它包含了字面量和符号引用。比如

    		 0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
             3: ldc           #3                  // String qsm
             5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
             8: return
    

    比如ldc指令指向了#3,而#3在常量池中,指的是#3 = String #26 // qsm#26指的是#26 = Utf8 qsm,也就是说这里的#3就是符号引用。这些符号引用到加载到jvm(解析阶段)或者运行时(虚拟机栈的动态链接),才会变成直接引用。

    问题4的答案,使用javap -v xxx.class命令就可以查看到常量池。

    问题5的答案,一个类编译为字节码中,肯定是需要了很多其他的类,比如本代码就需要了System,Object等类信息,这些数据很大,不可能全部放在一个类的字节码文件中,所以先使用常量池,对这些数据的引用使用符号引用,等真正加载到jvm或者运行时才将这些符号引用转换为直接引用。

    问题6、当类加载到内存中后,jvm就会将class常量池中的内容存放到运行时常量池中了。但是jdk1.8把其中的字符串常量池放入了jvm中。


    7、什么叫符号引用,什么叫直接引用
    8、什么地方进行了符号引用转换为直接引用
    8、什么叫静态链接和动态链接

    还是上面的javap解析出来的的字节码文件为例

    		 0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
             3: ldc           #3                  // String qsm
             5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
             8: return
    

    0: getstatic #2,第一句getstatic得到静态属性,里面的#2就是符号引用,在常量池中它又指向了其他符号引用,但最终实际代表了PrintStream类的一个对象。这些符号引用在编译的时候,仅仅是表面自己需要什么,到加载jvm或者运行时才执行真正的地址,这个就是直接引用了。

    jvm加载的时候有一个阶段叫做解析,这里就把能够确定的符号引用转变为直接引用了。这里的解析也成为静态链接。还有一个地方,线程的虚拟机栈执行方法的时候,有一个动态链接,这个地方也是将符号引用转为直接引用的,由于是在运行中,所以叫做动态链接。

    那么什么情况下使用静态链接,什么情况使用动态链接。一般静态链接的时候,都是能够直接确定地址的,比如类的静态方法,final方法,private方法,构造器等,这些都叫做非虚方法。
    而一般的public void hi();等成员方法就是虚方法。这些在加载的时候,是无法确定的。只有正运行的时候才知道。主要原因还是多态(重写)因素。
    比如,Father person= new SonA();,执行person.hi()的时候,是不知道这个hi方法具体是执行谁的,是SonA的还是Father的,并不清楚SonA是否重写了父类的hi方法。所以这里只有真正运行方法的时候,才知道才符号引用指向直接引用。
    再看5: invokevirtual #4,invokevirtual 就是调用虚方法,它下面还有子类。
    在这里插入图片描述

    【完,喜欢就点个赞呗】

    正在去BAT的路上修行

    展开全文
  • java虚拟机运行时常量池时间:20180228运行时常量池是方法区的一部分。Class文件(也就是.java文件编译后生成的字节码.class文件)中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池(Constant ...

    java虚拟机运行时常量池

    时间:20180228

    运行时常量池是方法区的一部分。Class文件(也就是.java文件编译后生成的字节码.class文件)中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用()。这部分内容将在类加载后进入方法区的运行时常量池中存放。

    常量池主要用于存放两大类常量:字面量(Literal)和符号引用量(Symbolic References),字面量相当于Java语言层面常量的概念,如文本字符串,声明为final的常量值等,符号引用则属于编译原理方面的概念,包括了如下三种类型的常量:

    类和接口的全限定名

    字段名称和描述符

    方法名称和描述符

    一般来说除了保存class文件中描述的符号引用外,还会把翻译出来的直接引用也存储在运行时常量池中。

    运行时常量池与Class文件常量池的区别

    运行时常量池相对于Class文件常量池的另外一个重要特征是具备动态性。运行期间也能将新的常量放入池中,比如String类的intern()方法。

    举例:

    package com.test5;

    public class Test {

    public static void main(String[] args) {

    String s1 = "abc";

    String s2 = "abc";

    System.out.println(s1 == s2);

    String s3 = new String("abc");

    String s4 = new String("abc");

    System.out.println(s1 == s2);

    //基本数据类型和抽象数据类型都会放在局部变量表中(虚拟机栈的栈帧中)

    //String 是抽象数据类型,抽象数据类型存放的是引用,

    System.out.println("s1 == s2? :" + (s1 == s2));

    //System.out.println("s1 == s2?" + s1 == s2);返回结果为 false为什么?

    System.out.println("s1 == s3? :" + (s1 == s3));

    System.out.println("s1 == s3? :" + (s1 == s3.intern()));

    System.out.println("s3 == s4? :" + (s3 == s4));

    }

    }

    结果:

    true

    true

    s1 == s2? :true

    s1 == s3? :false

    s1 == s3? :true

    s3 == s4? :false

    图解分析

    a380f727ff86?utm_campaign

    图解分析

    文字分析

    String s1 = "abc";

    String是抽象数据类型,而抽象数据类的引用(抽象数据类型变量)s1存放在局部变量表中。(此处时s1本身也就是局部变量,理应存放在局部变量表中???)。"abc"为字符串,字符串的创建都存放在运行时常量池。字符串为字面量(Literal),常量池主要存放两大类常量:字面量(Literal)和符号引用(Symbolic References)。

    String s2 = "abc";

    s2的分析同上。只是需要注意的是在常量池中存储的数据都是不重复的。此时s1和s2都指向常量池中同一块区域。因此s1==s2返回true;

    String s3 = new String("abc");

    而new创建的是对象实例,存储在堆中。s3指向堆中某一块区域,因此s1!=s3;

    展开全文
  • 一、运行时常量池简介运行时常量池(Runtime Constant Pool),它是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述等信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的...

    一、运行时常量池简介

    运行时常量池(Runtime Constant Pool),它是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述等信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到常量池中。

    运行时常量是相对于常量来说的,它具备一个重要特征是:动态性。当然,值相同的动态常量与我们通常说的常量只是来源不同,但是都是储存在池内同一块内存区域。Java语言并不要求常量一定只能在编译期产生,运行期间也可能产生新的常量,这些常量被放在运行时常量池中。这里所说的常量包括:基本类型包装类(包装类不管理浮点型,整形只会管理-128到127)和String(也可以通过String.intern()方法可以强制将String放入常量池)

    二、 Class文件中的信息常量池

    在Class文件结构中,最头的4个字节用于存储Megic Number,用于确定一个文件是否能被JVM接受,再接着4个字节用于存储版本号,前2个字节存储次版本号,后2个存储主版本号,再接着是用于存放常量的常量池,由于常量的数量是不固定的,所以常量池的入口放置一个U2类型的数据(constant_pool_count)存储常量池容量计数值。

    常量池主要用于存放两大类常量:字面量(Literal)和符号引用量(Symbolic References),字面量相当于Java语言层面常量的概念,如文本字符串,声明为final的常量值等,符号引用则属于编译原理方面的概念,包括了如下三种类型的常量:

    类和接口的全限定名

    字段名称和描述符

    方法名称和描述符

    三、 常量池的好处

    常量池是为了避免频繁的创建和销毁对象而影响系统性能,其实现了对象的共享。例如字符串常量池,在编译阶段就把所有的字符串文字放到一个常量池中。

    节省内存空间:常量池中所有相同的字符串常量被合并,只占用一个空间。

    节省运行时间:比较字符串时,==比equals()快。对于两个引用变量,只用==判断引用是否相等,也就可以判断实际值是否相等。

    双等号==的含义

    基本数据类型之间应用双等号,比较的是他们的数值。

    复合数据类型(类)之间应用双等号,比较的是他们在内存中的存放地址。

    四、 基本类型的包装类和常量池

    java中基本类型的包装类的大部分都实现了常量池技术,即Byte,Short,Integer,Long,Character,Boolean。这5种包装类默认创建了数值[-128,127]的相应类型的缓存数据,但是超出此范围仍然会去创建新的对象。 两种浮点数类型的包装类Float,Double并没有实现常量池技术。

    1)Integer与常量池

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    Integer i1 = 40;

    Integer i2 = 40;

    Integer i3 = 0;

    Integer i4 = new Integer(40);

    Integer i5 = new Integer(40);

    Integer i6 = new Integer(0);

    System.out.println("i1=i2 " + (i1 == i2));

    System.out.println("i1=i2+i3 " + (i1 == i2 + i3));

    System.out.println("i1=i4 " + (i1 == i4));

    System.out.println("i4=i5 " + (i4 == i5));

    System.out.println("i4=i5+i6 " + (i4 == i5 + i6));

    System.out.println("40=i5+i6 " + (40 == i5 + i6));

    i1=i2 true

    i1=i2+i3 true

    i1=i4 false

    i4=i5 false

    i4=i5+i6 true

    40=i5+i6 true

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    解释:

    Integer i1=40;Java在编译的时候会直接将代码封装成Integer i1=Integer.valueOf(40);,从而使用常量池中的对象。

    Integer i1 = new Integer(40);这种情况下会创建新的对象。

    语句i4 == i5 + i6,因为+这个操作符不适用于Integer对象,首先i5和i6进行自动拆箱操作,进行数值相加,即i4 == 40。然后Integer对象无法与数值进行直接比较,所以i4自动拆箱转为int值40,最终这条语句转为40 == 40进行数值比较。

    2)String与常量池-普通方法赋值

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    String str1 = "abcd";

    String str2 = new String("abcd");

    System.out.println(str1==str2);//false

    String str1 = "str";

    String str2 = "ing";

    String str3 = "str" + "ing";

    String str4 = str1 + str2;

    System.out.println("string" == "str" + "ing");// true

    System.out.println(str3 == str4);//false

    String str5 = "string";

    System.out.println(str3 == str5);//true

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    解释:

    "abcd"是在常量池中拿对象,new String("abcd")是直接在堆内存空间创建一个新的对象。只要使用new方法,便需要创建新的对象。

    连接表达式 +,只有使用引号包含文本的方式创建的String对象之间使用“+”连接产生的新对象才会被加入常量池中。

    对于字符串变量的“+”连接表达式,它所产生的新对象都不会被加入字符串池中,其属于在运行时创建的字符串,具有独立的内存地址,所以不引用自同一String对象。

    3)String与常量池-静态方法赋值

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    public static final String A; // 常量A

    public static final String B; // 常量B

    static {

    A = "ab";

    B = "cd";

    }

    public static void main(String[] args) {

    // 将两个常量用+连接对s进行初始化

    String s = A + B;

    String t = "abcd";

    if (s == t) {

    System.out.println("s等于t,它们是同一个对象");

    } else {

    System.out.println("s不等于t,它们不是同一个对象");

    }

    }

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    解释:

    s不等于t,它们不是同一个对象。A和B虽然被定义为常量,但是它们都没有马上被赋值。在运算出s的值之前,他们何时被赋值,以及被赋予什么样的值,都是个变数。因此A和B在被赋值之前,性质类似于一个变量。那么s就不能在编译期被确定,而只能在运行时被创建了。

    4)String与常量池-intern方法

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    public static void main(String[] args) {

    String s1 = new String("计算机");

    String s2 = s1.intern();

    String s3 = "计算机";

    System.out.println("s1 == s2? " + (s1 == s2));

    System.out.println("s3 == s2? " + (s3 == s2));

    }

    s1 == s2? false

    s3 == s2? true

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    解释:

    String的intern()方法会查找在常量池中是否存在一份equal相等的字符串,如果有则返回该字符串的引用,如果没有则添加自己的字符串进入常量池。

    5)String与常量池-延伸

    String s1 = new String("xyz"); //创建了几个对象?

    解释:

    考虑类加载阶段和实际执行时。

    类加载对一个类只会进行一次。”xyz”在类加载时就已经创建并驻留了(如果该类被加载之前已经有”xyz”字符串被驻留过则不需要重复创建用于驻留的”xyz”实例)。驻留的字符串是放在全局共享的字符串常量池中的。

    在这段代码后续被运行的时候,”xyz”字面量对应的String实例已经固定了,不会再被重复创建。所以这段代码将常量池中的对象复制一份放到heap中,并且把heap中的这个对象的引用交给s1 持有。

    这条语句创建了2个对象。

    展开全文
  • 运行时常量池vsclass文件中的常量池 运行时常量池,就是运行时候的常量池,它是在内存中。 方法区,内部包含了运行时常量池,它是在内存中。 字节码文件,内部包含了常量池,它是在文件中。 要...

    一 运行时常量池 vs class 文件中的常量池

    运行时常量池,就是运行时候的常量池,它是在内存中。

    • 方法区,内部包含了运行时常量池,它是在内存中。
    • 字节码文件,内部包含了常量池,它是在文件中。
    • 要弄清楚方法区,需要理解清楚 class 文件,因为加载类的信息都在方法区。
    • 要弄清楚方法区的运行时常量池,需要理解清楚 class 文件中的常量池。

    二 class 文件中常量池

    一个有效的字节码文件中除了包含类的版本信息、字段、方法以及接口等描述符信息外,还包含一项信息就是常量池表(Constant Pool Table),包括各种字面量和对类型、域和方法的符号引用。

    三 为什么需要常量池

    一个 Java 源文件中的类、接口,编译后产生一个字节码文件。而 Java 中的字节码需要数据支持,通常这种数据会很大以至于不能直接存到字节码里,换另一种方式,可以存到常量池,这个字节码包含了指向常量池的引用。在动态链接的时候会用到运行时常量池。
    public class SimpleClass {
        public void sayHello() {
            System.out.println("hello");
        }
    }
    虽然上述代码只有 194 字节,但是里面却使用了String、System、PrintStream 及 Object 等结构。这段代码的代码量其实很少了,如果代码多的话,引用的结构将会更多,这里就需要用到常量池了。

    四 常量池中有什么

    • 数量值
    • 字符串值
    • 类引用
    • 字段引用
    • 方法引用
    public class MethodAreaTest2 {
        public static void main(String args[]) {
            Object obj = new Object();
        }
    }
    Object obj = new Object();
    会被翻译成如下字节码
    new #2  
    dup
    invokespecial

    五 运行时常量池

    • 运行时常量池(Runtime Constant Pool)是方法区的一部分。
    • 常量池表(Constant Pool Table)是Class文件的一部分,用于存放编译期生成的各种字面量与符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。
    • 在加载类和接口到虚拟机后,就会创建对应的运行时常量池。
    • JVM为 每个已加载的类型(类或接口)都维护一个常量池。池中的数据项像数组项一样,是通过索引访问的。
    • 运行时常量池中包含多种不同的常量,包括编译期就已经明确的数值字面量,也包括到运行期解析后才能够获得的方法或者字段引用。此时不再是常量池中的符号地址了,这里换为真实地址。
    • 运行时常量池,相对于 class 文件常量池的另一重要特征是:具备动态性。
    • 运行时常量池类似于传统编程语言中的符号表(symboltable),但是它所包含的数据却比符号表要更加丰富一些。
    • 当创建类或接口的运行时常量池时,如果构造运行时常量池所需的内存空间超过了方法区所能提供的最大值,则 JVM 会抛 OutOfMemoryError 异常。

    六 小结

    常量池可以看做是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等类型。
    展开全文
  • 全局字符串里的内容是在类加载完成,经过验证,准备阶段之后在堆中生成字符串对象实例,然后将该字符串对象实例的引用值存到string pool中(记住:string pool中存的是引用值而不是具体的实例对象,具体的实例对象...
  • 常量池 一个有效的字节码文件中除了包含的版本信息,字段,方法以及接口等描述符等信息外,还包含常量池表Constant Poll Table,包括各种字面...在动态链接的时候会使用到运行时常量池 public class SimpleClass { publi
  • Java中的常量池,实际上分为两种形态:静态常量池运行时常量池。1)所谓静态常量池,即*.class文件中的常量池,class文件中的常量池不仅仅包含字符串(数字)字面量,还包含类、方法的信息,占用class文件绝大部分...
  • 当类加载到内存中后,jvm就会将class常量池中的内容存放到运行时常量池中,经过解析(resolve)之后,也就是把符号引用替换为直接引用,解析的过程会去查询全局字符串池,也就是下面的StringTable,以保证运行时常量...
  • 首先明确class常量池运行时常量池、字符串常量池不是同一个概念。 常量池表(Constant Pool Table) 常量池表(Constant Pool Table)是Class文件(字节码文件)的一部分,在编译阶段,用于存放编译期生成的各种...
  • 方法区 方法区是 JVM 规范中提出一个概念,不同的 Java 虚拟机都有自己的方法区,但是对方法区的具体实现...到了 JDK 7 开始逐步废弃永久代,首先将永久代中的常量、静态变量、字符串常量池移动到了 Java 堆中。 到了
  • 字符串池、运行时常量池、Class常量池首先了解一下java内存模型:Java虚拟机内存区域划分图:区域是否线程共享是否会内存溢出程序计数器否不会java虚拟机栈否会本地方法栈否会堆是会方法区是会1. 程序计数器(Program...
  • 运行时常量池是一个统称 也包括字符串常量池,但是字符串常量池放的只是字符串,而运行时常量池中,还包括类信息,属性信息,方法信息,以及其他基础类型的的常量池比如int,long等 jdk1.7之前,运行时常量池(包含着字符串...
  • 运行常量池指的是jvm运行中,将编译后的类放在metaspace区,具体包括class文件辕信息描述、编译后的代码数据、引用类型数据、类文件常量池等。在metaspace区 字符串常量池指的是类加载完成后,经过验证、准备阶段...
  • class常量池运行时常量池的区别 class常量池 一个java源文件的类,接口,编译后会产生一个字节码文件。而Java中的字节码需要数据支持,通常这种数据会很大以至于不能直接存到字节码里,换一种方式,可以存到常量池...
  • 运行时常量区就是当前运行类加载所有相关类(如父类、实现类、一些系统类、输出流等)的字节码文件后,把他们所有的静态常量池的数据汇总到一起,存放在该进程的运行时常量池中,再加上该类运行期解析后才能够获得的...
  • 这几天在看Java虚拟机方面的知识,看到了有几种不同常量池的说法,然后我就去CSDN、博客园等上找资料,里面说的内容真是百花齐放,各自争艳,因此,我好好整理了一下,将我自认为对的理解写下来与大家共同探讨: ...
  • 字符串常量池、class文件常量池运行时常量池的区别 1、Class文件常量池 class文件常量池就是在class文件十大组成部分中之一 classs文件十大组成部分 1.魔数 2.版本号 3.常量池(class文件常量池) 4.类访问标记...
  • 在class文件中,“常量池”是最复杂也最值得关注的内容。Java是一种动态连接的语言,常量池的作用非常重要,常量池中除了包含代码中所定义的各种基本类型(如int、long等等)和对象型(如String及数组)的常量值还,还...
  • 运行时常量池是方法区的一部分,方法区用于存放Class的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。String.intern()是一个native方法,它的作用是:如果字符串常量池中已经包含了一个等于此String...
  • 在 JDK 1.7 之前 如果要向运行时常量池中添加内容,最简单的做法就是...由于常量池分配在方法区内,我们可以通过-XX:PermSize 和-XX:MaxPermSize 限制方法区的大小,从而间接限制其中常量池的容量代码运行时常量池
  • 运行时常量池是方法区的一部分,方法区用于存放Class的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。String.intern()是一个native方法,它的作用是:如果字符串常量池中已经包含了一个等于此String...
  • 1、Java内存模型Java虚拟机在执行程序把它管理的内存分为若干数据区域,这些数据区域分布情况如下图所示:程序计数器:一块较小内存区域,指向当前所执行的字节码。如果线程正在执行一个Java方法,这个计数器记录...
  • JVM常量池详解:深入理解Class常量池运行时常量池、字符串常量池一、Class常量池2.1字面量2.2符号引用二、运行时常量池三、字符串常量池3.1设计思想3.2设计原理3.3字符串常量池位置3.4三种字符串操作 一、Class...
  • 简介JVM在运行的时候会对class文件进行加载,链接和初始化的过程。class文件中定义的常量池在JVM加载之后会发生什么神奇的变化呢?快来看一看吧。...运行时常量池但是只有class文件中的常量池肯定是不...
  • 常用操作 制作好.9图打包到APK中直接使用(基本操作) 动态加载本地、服务器的.9图 使用代码动态创建.9图 加载.9图流程 .9图是在原图基础上,上下左右方向各加了1px,同时通过使用黑线(#FF000000)标记,规定图片的...
  • 看《深入理解java虚拟机》了解到方法区中存在运行时常量池(Runtime Constant Pool),运行时常量池会在类加载载入class文件中的常量池信息(constant_pool table)。public class Test{public static String a = "a...
  • Java中的常量池,实际上分为两种形态:静态常量池运行时常量池。 静态常量池就是你编译生成的.class文件里面存放的数据,静态常量池主要存放:字面量和符号引用,所谓字面量就是一些文本字符串,被声明为final...
  • 首先:要弄清楚常量池运行时常量池和字符串常量池(StringTable)三者之间的关系和特性: 三者之间的关系: 1、常量池: 俗称静态常量池,又称常量池表(Constant Pool Table),存在于*.class文件中,就是一张表,...
  • 文章目录1、引言2、Class常量池3、运行时常量池3.1 基本类型的包装类3.2 String.intern()4、字符串常量池4.1 直接用双引号创建字符串4.2 使用new关键字创建字符串4.3 组合4.4 总结 1、引言 2、Class常量池 当 ....

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 131,283
精华内容 52,513
关键字:

运行时常量池