精华内容
下载资源
问答
  • C语言中的文字常量区与栈区分析 C语言中的文字常量区与栈区分析 C语言中的文字常量区与栈区分析 C语言中的文字常量区与栈区分析 C语言中的文字常量区与栈区分析 C语言中的文字常量区与栈区分析 C语言中的文字常量区...
  • Java中的常量池分为三种类型: 类文件中常量池(The Constant Pool) 运行时常量池(The Run-Time Constant Pool) ...class常量池是在编译的时候每个class都有的,在编译阶段,存放的是常量的符号引用...

     

    Java中的常量池分为三种类型:

    • 类文件中常量池(The Constant Pool)
    • 运行时常量池(The Run-Time Constant Pool)
    • String常量池

     

    类文件中常量池 ---- 存在于Class文件中

    所处区域:堆

    诞生时间:编译时

    内容概要:符号引用和字面量

    class常量池是在编译的时候每个class都有的,在编译阶段,存放的是常量的符号引用。

    常量池中存放的是符号信息,java虚拟机在执行指令的时候会依赖这些信息。常量池中的所有项都具有如下通用格式:

    cp_info {
     u1 tag;     //表示cp_info的单字节标记位
     u1 info[];  //两个或更多的字节表示这个常量的信息,信息格式由tag的值确定
    }
    Constant Type Value
    CONSTANT_Class 7
    CONSTANT_Fieldref 9
    CONSTANT_Methodref 10
    CONSTANT_InterfaceMethodref 11
    CONSTANT_String 8
    CONSTANT_Integer 3
    CONSTANT_Float 4
    CONSTANT_Long 5
    CONSTANT_Double 6
    CONSTANT_NameAndType 12
    CONSTANT_Utf8 1
    CONSTANT_MethodHandle 15
    CONSTANT_MethodType 16
    CONSTANT_InvokeDynamic 18

    举几个典型的例子来说明常量池中数据是如何存储的:

    CONSTANT_Class结构 -- 表示类或者接口,他的格式如下:

    CONSTANT_Class_info {
     u1 tag;       //这个值为 CONSTANT_Class (7)
     u2 name_index;//注意这是一个index,他表示一个索引,引用的是CONSTANT_UTF8_info
    }

    注意观察 这个CONSTANT_Class_info类型的常量内部结构是由一个tag(CONSTANT_Class(7))和一个name_index组成,name_index中注意这个index,他表示一个索引的,什么的索引呢?CONSTANT_Utf8_info结构的索引,这个结构用来表示一个有效的类或者接口的二进制名称的内部形式。class文件结构中出现的类或者接口名称都是通过全限定形式来表示的,也被称作二进制名称【题外话:全限定类名含义就类似 java.lang包中定义的Object类的完全限定名称为java.lang.Object】。

    那我们接着看CONSTANT_Utf8_info结构,他用于表示字符常量的值,他的结构如下所示:

    CONSTANT_Utf8_info {
     u1 tag;
     u2 length;
     u1 bytes[length];
    }
    

    我们注意到第一个tag肯定表示为:CONSTANT_Utf8(1);后面的length指明了bytes[]数组的长度;最后一个bytes[]数组引用了上一个length作为其长度。字符常量采用改进过的UTF-8编码表示。

     

    运行时常量池 ---- 存在于内存的元空间中

    诞生时间:JVM运行时

    内容概要:class文件元信息描述,编译后的代码数据,引用类型数据,类文件常量池。

    所谓的运行时常量池其实就是将编译后的类信息放入运行时的一个区域中,用来动态获取类信息。

    运行时常量池是在类加载完成之后,将每个class常量池中的符号引用值转存到运行时常量池中,也就是说,每个class都有一个运行时常量池,类在解析之后,将符号引用替换成直接引用,与全局常量池中的引用值保持一致。

    字符串常量池 ---- 存在于堆中

    从上述结果可以看出,JDK 1.6下,会出现“PermGen Space”的内存溢出,而在 JDK 1.7和 JDK 1.8 中,会出现堆内存溢出,并且 JDK 1.8中 PermSize 和 MaxPermGen 已经无效。因此,可以大致验证 JDK 1.7 和 1.8 将字符串常量由永久代转移到堆中,并且 JDK 1.8 中已经不存在永久代的结论。

    字符串池里的内容是在类加载完成,经过验证,准备阶段之后在堆中生成字符串对象实例,然后将该字符串对象实例的引用值存到string pool中(记住:string pool中存的是引用值而不是具体的实例对象,具体的实例对象是在堆中开辟的一块空间存放的)。 在HotSpot VM里实现的string pool功能的是一个StringTable类,它是一个哈希表,里面存的是驻留字符串(也就是我们常说的用双引号括起来的)的引用(而不是驻留字符串实例本身),也就是说在堆中的某些字符串实例被这个StringTable引用之后就等同被赋予了”驻留字符串”的身份。这个StringTable在每个HotSpot VM的实例只有一份,被所有的类共享。

    https://www.cnblogs.com/dennyzhangdd/p/6770188.html

     

    展开全文
  • String类和常量池内存分析例子以及8种基本类型

    千次阅读 多人点赞 2019-01-21 19:52:51
    该篇例子几乎涵盖了目前所有能解决的...String类和常量池内存分析 8种基本类型的包装类和常量池 String 类和常量池 1 String 对象的两种创建方式 String str1 = "abcd"; String str2 = new String("abcd"); Sy...

    该篇例子几乎涵盖了目前所有能解决的类型,以一种通俗的语言讲解出来。当然如果JVM内存基本问题不太会可以看这里:JVM内存的基本问题

    目录

    String类和常量池内存分析

    说说String.intern()

    8种基本类型的包装类和常量池


     

    String类和常量池内存分析

    1 String 对象的两种创建方式

    String str1 = "abcd";

    String str2 = new String("abcd");

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

    记住:只要使用 new 方法,便需要创建新的对象。

    2 String 类型的常量池比较特殊。它的主要使用方法有两种:

    • 直接使用双引号声明出来的 String 对象会直接使用或创建常量池中对应的字符串。
    • 如果不是用双引号声明的 String 对象,可以使用 String 提供的 intern 方法。

    说说String.intern()

    String.intern() 是一个 Native 方法,它的作用(在JDK1.6和1.7操作不同)是:

    如果运行时常量池中已经包含一个等于此 String 对象内容的字符串,则直接返回常量池中该字符串的引用;

    如果没有, 那么

    在jdk1.6中,将此String对象添加到常量池中,然后返回这个String对象的引用(此时引用的串在常量池)。

    在jdk1.7中,放入一个引用,指向堆中的String对象的地址,返回这个引用地址(此时引用的串在堆)。根据《java虚拟机规范 Java SE 8版》记录,如果某String实例所包含的Unicode码点序列与CONSTANT——String_info结构所给出的序列相同,而之前又曾在该实例上面调用过String.intern方法,那么此次字符串常量获取的结果将是一个指向相同String实例的引用。这是什么意思呢?如果看不懂这个例子没关系,跟着后面从问题一到问题八看一遍你就一定能明白的!!

            String a1 = new String("a"); // 创建"a",并把"a"缓存到常量池,占用2块内存空间
            String a2 = new String("b"); // 创建"b",并把"b"缓存到常量池,占用2块内存空间
            String a3 = new String("c"); // 创建"c",并把"c"缓存到常量池,占用2块内存空间
            System.out.println((a1+a2+a3).intern() == "abc");  // true,在堆中连接生成了"abc",调用intern()后发现常量池没有"abc",那么就把指向堆中的"abc"的引用放入常量池,返回值为这个引用。当执行这个表达式的时候,从左到右执行,左边是常量池指向堆中的引用,右边也是直接使用常量池的"abc"(其实就是指向堆中"abc"的引用),直接返回这个引用,两边引用都是堆里面连接生成的"abc"的地址,所以打印出true。

    Unicode码点序列的直接理解:这玩意不就是字符连起来看equals是否相同不就完了吗!咋这么墨迹~

    关于String的intern()问题,可参考这篇文章Java技术——你真的了解String类的intern()方法吗

    关于运行时常量池:java虚拟机为每个类型都维护着一个常量池。该常量池是java虚拟机中的运行时数据结构,像传统编程语言实现中的符号表一样有很多用途。当类或接口创建时,它的二进制表示中的常量池表被用来构造运行时常量池,运行时常量池中的所有引用最初都是符号引用。

    以下所说常量池为字符串常量池。

    接下来我们均以示例的方式来解释问题,也是我在某篇文章底下解决的别人问题的笔记。

    可能最颠覆你认知的是问题八,所以既然来看了,还是建议看到最后吧!

    问题一:

            String h = new String("cc");
            String intern = h.intern();
            System.out.println(intern == h); // 返回false

    这里为什么不返回true,而是返回false呢?

    解释:

    new String("cc")后,堆中创建了"cc","cc"也会缓存到常量池,可以认为占用了2个字符串对象内存(因为你创建了一个“cc”字符串对象,但是放到了2个地方占用了2块内存)!当你String intern = h.intern();其中h.intern()会去常量池检查是否有了"cc",结果发现有了,那么此时返回常量池的引用地址给intern,用常量池的引用intern和堆中的h引用去比较肯定不相等。所以返回false。

    在JDK中String也是类似于消息池,就是典型的享元模式,一个String被定义后就被缓存到常量池,当其他地方要使用同样的字符串时,就直接使用缓存而不是重复创建。

    问题二:

    我对以下代码的操作过程有疑问

    String str2 = new String("str") + new String("01");
    String str1 = "str01";
    str2.intern();
    System.out.println(str2 == str1); // false

    解释:

           第一句new String("str") + new String("01");现在在堆中创建了"str",同时"str"缓存到常量池,创建了"01",同时"01"也缓存到常量池,再进行连接,堆中出现了"str01"。此时常量池中有:"str","01",此时堆中有"str","01","str01"。str2引用指向堆中的"str01"。
           接着第二句String str1 = "str01"; 发现常量池没有"str01",那么直接在常量池创建"str01"。此时常量池中有:"str","01","str01",此时堆中有"str","01","str01"。str1指向常量池中的"str01"。
           接着第三句str2.intern();检查常量池是否有"str01",结果发现有了,返回常量池"str01"的地址,很可惜,没有变量去接收,所以这一句没什么用,str2指向也不会改变,还是指向堆中"str01"。 
           第四句去打印str2==str1,一个堆中的"str01"地址和一个常量池中的"str01"地址比较,返回false。

    问题三:

    那这一段代码呢?

            String str2 = new String("str") + new String("01");
            String str1 = "str01";
            String str3 = str2.intern();
            System.out.println(str3 == str1); // true

    解释:

    比问题二多了一个str3引用保存了常量池"str01",str3和str1均指向常量池的"str01",所以返回true

    问题四:

            String str2 = new String("str") + new String("01");
            str2.intern();
            String str1 = "str01";
            System.out.println(str2 == str1);
    
            String str3 = new String("str01");
            str3.intern();
            String str4 = "str01";
            System.out.println(str3 == str4);

    这个代码的过程晕乎了,到底这些串在堆还是在常量池呢?

    解释:

           第一句new String("str") + new String("01");现在在堆中创建了"str",同时"str"缓存到常量池,创建了"01",同时"01"缓存到常量池,再进行连接,堆中出现了"str01"。此时常量池中有:"str","01",此时堆中有"str","01","str01"。str2引用指向堆中的"str01"。 
           第二句,str2.intern();检查到常量池不存在"str01",如果在jdk1.6,那么就将堆中的"str01"添加到常量池中,如果是jdk1.7,那么就在常量池保存指向堆中"str01"的地址,即保存堆中"str01"的引用。接下来的讲解以jdk1.7为准!!所以这里是在常量池保存了堆中"str01"的引用。

           第三句,String str1 = "str01";检查到常量池有一个引用保存了这个串,str1就直接指向这个地址,即还是堆中的"str01"。

           第四句,str2==str1是否相等,str2指向堆中的"str01",str1指向常量池的某个地址,这个地址恰好是指向堆中的"str01",所以仍然是true。

           第五句,String str3 = new String("str01");又在堆中创建了"str01",现在堆中有了2个"str01",而常量池已经有"str01"引用,不再缓存进去。(结论是常量池有equals相同的串或者引用指向equals相同的串就不再缓存)

           第六句,str3.intern(); 去检查一下常量池到底有没有"str01"呢?检查发现常量池有个引用指向堆中的"str01",JVM认为常量池是有"str01"的,那么直接返回指向堆中的"str01"地址,很可惜,没有变量去接收,这一句在这里没有什么用。

           第七句,String str4 = "str01";检查到常量池有个引用指向堆中的"str01",那么str4也保存这个引用,所以这个"str01"还是堆中的第一个"str01"。

           第八句,打印str3==str4,str3是堆中新建的第二个"str01",str4保存引用指向第一个堆中的"str01",两块堆的地址,所以返回false。

    问题五:

     String str2 = new String("str") + new String("01");

    为什么不String str2 = new String("str01");呢? 区别在哪里呢?

    解释:

           我们来单独执行比较,前者new String("str")堆中创建"str",同时"str"缓存到常量池,new String("01")在堆中创建"01",同时"01"缓存到常量池,相加操作只会在堆中创建"str01",所以前者执行以后,内存:堆中有"str","01","str01",常量池中"str","01"。str2引用指向堆中的"str01"。
           现在来看后者String str2 = new String("str01");这个就是在堆中创建"str01"同时"str01"缓存到常量池,str2引用指向堆中的"str01",内存:堆中有"str01",常量池中有"str01"。
           综上所述,区别就在于这些串处于不同的位置,前者在常量池是没有"str01"的。

    问题六:

            String s = new String("abc"); 
            String s1 = "abc"; 
            String s2 = new String("abc"); 
            System.out.println(s == s1);// 堆内存"abc"和常量池"abc"相比,false
            System.out.println(s == s2);// 堆内存s和堆内存s2相比,false
            System.out.println(s == s1.intern());// 堆内存"abc"和常量池"abc"相比,false
            System.out.println(s == s2.intern());// 堆内存"abc"和常量池"abc"相比,false
            System.out.println(s1 == s2.intern());// 常量池"abc"和常量池"abc"相比,true
            System.out.println(s.intern() == s2.intern());// 常量池"abc"和常量池"abc"相比,true

    解释:有注释,无需多余解释,上面的问题看懂了这个一看就懂。

    问题七:

            String s1 = "abc"; 
            String s2 = "a"; 
            String s3 = "bc"; 
            String s4 = s2 + s3; 
            System.out.println(s1 == s4);//false,因为s2+s3实际上是使用StringBuilder.append来完成,会生成不同的对象。
            // s1指向常量池"abc",s4指向堆中"abc"(append连接而来)
            String S1 = "abc"; 
            final String S2 = "a"; 
            final String S3 = "bc"; 
            String S4 = S2 + S3; 
            System.out.println(S1 == S4);//true,因为final变量在编译后会直接替换成对应的值
            // 所以实际上等于s4="a"+"bc",而这种情况下,编译器会直接合并为s4="abc",所以最终s1==s4为true。

    问题八:

            String str1 = "abcd"; // 常量池创建"abcd"
            String str2 = "abcd"; // str2还是上一步的"abcd"
            String str3 = "ab" + "cd"; // 常量池创建"ab"和"cd",连接过程编译器直接优化成"abcd",而常量池已经有了"abcd",所以str3和str1都指向"abcd"
            String str4 = "ab"; // 常量池已经有了"ab"
            str4 += "cd"; // str4+"cd"连接的字符串编译器不能优化,所以此时str4指向堆中的"abcd"
            // 因为"ab"是str4引用的,如果是两个变量s1="ab", s2="cd",s1+s2连接,那么只有用final修饰的指向"ab"的s1和final修饰的指向"cd"的s2相连接才能优化成"abcd"
            // 如果只有一个变量s1和常量池的常量连接s1+"cd",这个变量s1也需要final修饰才会优化成"abcd"
            System.out.println(str1 == str2); // true
            System.out.println(str1 == str3); // true
            System.out.println(str1 == str4); // false
            System.out.println("================");
            String s1 = "a";
            String s2 = "b";
            String s3 = "ab";
            final String ss1 = "a";
            final String ss2 = "b";
            System.out.println(s1 + s2 == s3); // false, 有变量引用的字符串是不能优化的,除非变量是final修饰或者直接"a"+"b"的常量形式,这一行就是s1+s2生成堆里的"ab"和常量池的"ab"在比较
            System.out.println(ss1 + ss2 == s3); // true,原因见上一行,原理和下一行相同,都是常量连接
            System.out.println("a" + "b" == s3); // true,常量池的"a"和"b"连接,根据Copy On Write机制, 副本连接生成"ab",发现已存在,直接指向"ab",所以和s3相等
            

    验证一下确实生成了副本才进行连接: 

            String s = "ab";    // 常量池创建"ab"
            String s1 = new String("ab"); // 堆里面创建"ab",因为常量池已有"ab",不会在常量池再缓存一次
            String str3 = "ab" + "cd"; // 连接之后常量池是否还有"ab"??在常量池连接成"abcd"后"ab"和"cd"是否还存在?
            String s2 = s1.intern(); // 如果常量池还有"ab",s2指向常量池"ab",如果没有,则放入s1地址,s2就指向s1,即s2指向堆里的"ab"
            System.out.println(s2 == s1); // 如果true,则s2是堆里的"ab".说明"ab"+"cd"连接后,常量池只有"abcd","ab"和"cd"被回收了
            // 结果运行出来是false,说明"ab"+"cd"连接之后,不仅存在"ab","cd", 还存在"abcd"

           关于Java的String类这种在修改享元对象时,先将原有对象复制一份,然后在新对象上再实施修改操作的机制称为“Copy On Write”,大家可以自行查询相关资料来进一步了解和学习“Copy On Write”机制,在此不作详细说明。

    上面"ab"+"cd"就是"ab"生成"副本ab", "cd"生成"副本cd","副本ab" + "副本cd"被编译器优化成了"abcd",此时优化的副本不再存在,常量池就是"ab", "cd", "abcd"

     

    ("a"+"b"+"c").intern() == "abc"; //true

    "a"+"b"+"c" == "abc"; //true

    看看大家有没有理解?不妨再回到文章开头的例子看看自己能不能分析出来了。

     

    8种基本类型的包装类和常量池

    • Java 基本类型的包装类的大部分都实现了常量池技术,即 Byte、Short、Integer、Long、Character、Boolean;这6种包装类会有相应类型的缓存数据,但是超出此范围仍然会去创建新的对象。Byte、Short、Integer、Long缓存[-128, 127]区间的数据,Character缓存[0, 127]区间的数据,Boolean缓存true和false这两个Boolean对象。
    • 两种浮点数类型的包装类 Float、Double 并没有实现常量池技术。

    首先大家要知道自动装箱直接赋值就可以,比如 Integer a = 20;

    手动装箱有2种方式,一个是调用构造方法Integer a = new Integer(20);另一个是valueOf方法,Integer a = Integer.valueOf(20);

    为什么给大家强调手动装箱?知道调用valueOf,不就可以去看源码在做什么了吗?

    Integer i1 = 33;
    Integer i2 = 33;
    System.out.println(i1 == i2);// 输出true 
    Integer i11 = 333;
    Integer i22 = 333; 
    System.out.println(i11 == i22);// 输出false 
    Double i3 = 1.2; 
    Double i4 = 1.2; 
    System.out.println(i3 == i4);// 输出false
    Double i5 = Double.valueOf(100);
    Double i6 = Double.valueOf(100);
    System.out.println(i5 == i6);// 输出false

    在[-128,127]区间内的利用cache数组的值,否则new一个新的Integer对象。这里2个333不等因为是2块不同的堆内存。2个33相等是因为利用了同一个cache数组,是值的比较,这里i1==33,打印出来也是true。

    Integer 缓存源代码:

        public static Integer valueOf(int i) {
            if (i >= IntegerCache.low && i <= IntegerCache.high) // Integer里面的high值可以配置,默认127,具体见源码
                return IntegerCache.cache[i + (-IntegerCache.low)];
            return new Integer(i);
        }

    看源码可以知道除了Float、Double,其他基本类型的包装类都有对应的对象常量池缓存(就是cache数组缓存-128~127),Float、Double不管自动还是手动装箱,一定不相等,里面都是调用构造new出来的,比较2块堆内存,请自行查看valueOf源码验证。

        public static Double valueOf(double d) {
            return new Double(d);
        }

    应用场景:

    1. Integer i1=40;Java 在编译的时候会直接将代码封装成 Integer i1=Integer.valueOf(40); 从而使用常量池中的对象。
    2. Integer i1 = new Integer(40) ;这种情况下会创建新的对象。

    Integer i1 = 40; 
    Integer i2 = new Integer(40); 
    System.out.println(i1==i2); //输出false

    Integer 比较(==)更丰富的一个例子:

    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

    解释:

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

     

    关注、留言,我们一起学习。 
     

    ===============Talk is cheap, show me the code================

    展开全文
  • 易语言源码_常数_常量和资源例程及分析 易语言源码_常数_常量和资源例程及分析 易语言源码_常数_常量和资源例程及分析
  • 享元设计模式-深入再看String常量池(实例分析: String常量池详解)
    展开全文
  • 局部变量和常量的性能分析

    千次阅读 2017-07-20 10:24:05
    下面从字节码角度分析Test类中的test1方法和test2方法的局部变量表。 从字节码从11到21之间是for循环体,可以看出19行ldc指令是每次都是从常量池中获取Integer.MAX_VALUE的值。 字节码15-25之间是for...

    前两天群里有人问,下面的代码1 比代码2运行时间上慢了100ms,这是问什么?
    请看下面的两个代码片段:

    代码1

    for(int i=0; i<Integer.MAX_VALUE; i++){
        sum+=i;
    }

    代码2

    for(int i=0, len=Integer.MAX_VALUE; i<len; i++){
        sum+=i;
    }

    我本地使用的JDK1.8执行的,但每次执行这两段的时间基本一样。

    下面代码是我执行的测试代码:

    代码3

    public class Test {
        public static void main(String[] args) {
            test1();
            test2();
        }
        public static int test1() {
            long start = System.currentTimeMillis();
            int sum = 0;
            for (int i = 0; i < Integer.MAX_VALUE; i++) {
                sum += i;
            }
            long end = System.currentTimeMillis();
            System.out.println(end - start);
            return sum;
        }
    
        public static int test2() {
            long start = System.currentTimeMillis();
            int sum = 0;
            for (int i = 0, len = Integer.MAX_VALUE; i < len; i++) {
                sum += i;
            }
            long end = System.currentTimeMillis();
            System.out.println(end - start);
            return sum;
        }
    }

    Paste_Image.png

    发现每次执行时间差不多,不会出现上述的差别的。我使用的JDK1.8。可能别的版本的JDK有此问题。

    下面我又通过添加JVM编译模式参数运行程序得到如下结果:

    -Xint:全部使用字节码解释运行
    -Xcomp:全部被编译成机器码执行
    -Xmixed: 使用混合编译,jdk1.8默认执行方式

    Paste_Image.png

    从执行结果我们发现
    -Xcomp 和 -Xmixed模式:编译成本地机器码后执行的效率是一样的,没啥区别
    -Xint模式:解释执行两个方法执行时间差距大概2秒

    为什么-Xint模式下会有2秒的时间差距呢?

    代码1和代码2的区别
    代码1:在for循环中直接使用常量Integer.MAX_VALUE进行对比。
    代码2:在定义一个len变量赋值为Integer.MAX_VALUE,在for循环中使用len局部变量进行对比。
    区别一个引用的是全局常量,一个引用的是局部变量

    局部变量存储在栈的局部变量表中
    常量存储在方法区的常量池中(jdk1.7或之前叫方法区,jdk1.8叫元空间)

    下面从字节码角度分析Test类中的test1方法和test2方法的局部变量表。
    test1方法
    从字节码从11到21之间是for循环体,可以看出19行ldc指令是每次都是从常量池中获取Integer.MAX_VALUE的值。

    ldc指令说明

    test2方法
    字节码15-25之间是for循环,这个方法,我们可以看到在for循环外面先从常量池中获取Integer.MAX_VALUE的值赋值给本地变量,for循环体内每次进行比较的是本地变量,也就是局部变量表中的值,而不是每次从常量池获取变量的值。这就是test2方法比test1方法快的原因。

    本人简书blog地址:http://www.jianshu.com/u/1f0067e24ff8    
    点击这里快速进入简书

    GIT地址:http://git.oschina.net/brucekankan/
    点击这里快速进入GIT

    展开全文
  • 第一种方式创建实例时,JVM会首先检查字符串常量池中是否含有该实例。如果字符串已经存在池中,就返回池中的实例引用。如果字符串不在池中,就会实例化一个字符串并放到池中。(8种基本数据类型均有常量池机制)。 ...
  • JVM常量池及字符串==比较分析

    千次阅读 2017-03-02 16:51:58
    Java常量池技术java中的常量池技术,是为了方便快捷地创建某些对象而出现的,当需要一个对象时,就可以从池中取一个出来(如果池中没有则创建一个),则在需要重复创建相等变量时节省了很多时间。常量池其实也就是一...
  • 编译原理_常量定义语句语法分析

    千次阅读 2014-03-10 17:35:57
     判断常量语句定义是否合法,合法则输出分析结果,不合法则输出原由。 二、不合法原由  1、是否有关键字“const”开头定义  2、是否有“;”结束定义;  3、句子中格式行的空格处理;  4、常量名是否...
  • 常量指针和指针常量的区别 常量指针、指针常量以及指向常量的指针常量概念简要描述
  • msp430 iar 堆栈分析 常量数组优化

    千次阅读 2012-11-28 09:42:16
    常量数组定义在函数内部时,函数运行时需要将常量数组拷贝到堆栈,浪费了堆栈空间,还增加了函数#?CopyMemoryBytes的时间开销。...对比分析常量数组定义在函数内部 static void updata_lead_num(void) {
  • 指针常量常量指针

    2015-01-24 17:26:10
    用中国话的语义分析就可以很方便地把三个概念区分开。 一) 常量指针。 常量是形容词,指针是名词,以指针为中心的一个偏正结构短语。这样看,常量指针本质是指针,常量修饰它,表示这个指针乃是一个指向常量的...
  • 常量指针 指针常量

    2013-03-31 13:23:28
    用中国话的语义分析就可以很方便地把三个概念区分开。 一) 常量指针。 常量是形容词,指针是名词,以指针为中心的一个偏正结构短语。这样看,常量指针本质是指针,常量修饰它,表示这个指针乃是一个指向常量的...
  • 常量,字符串常量

    2013-10-31 18:59:42
    ... 对于普通变量常量:"常量折叠"是 就是在编译器进行语法分析的时候,将常量表达式计算求值,并用求 得的值来替换表达式,放入常量表。可以算作一种编译优化 #include void main() {
  • 常量

    千次阅读 2019-05-01 13:04:09
    介绍常量池之前先了解一个命令:javap 下面说一下常用指令: javap -s 查看目标类的内部类型签名信息(描述符),包括:字段类型签名,方法的类型签名 javap -c 对目标class反编译,可以查看指令的执行过程。 javap ...
  • c++常量

    2016-04-26 21:27:08
    数值常量 数值常量就是通常所说的常数。在C++中,数值常量是区分类型的,从字面形式即可识别其类型。 整型常量(整数)的类型 在上一节中已知道:整型数据可分为int, short int,long int以及unsigned int, ...
  • const char *p =&...首先此处 const 修饰的是 *p, 理解思路:p指向地址中的数据不能改变,但是p是可以改变的,所以p还可以行使指针的行为,所以是指针, 即常量指针。 指针常量:char* const p; 此处修饰...
  • 字符串常量池、class常量池和运行时常量

    万次阅读 多人点赞 2017-06-12 20:09:36
    在java的内存分配中,经常听到很多关于常量池的描述,我开始看的时候也是看的很模糊,网上五花八门的说法简直太多了,最后查阅各种资料,终于算是差不多理清了,很多网上说法都有问题,笔者尝试着...
  • 用中国话的语义分析就可以很方便地把三个概念区分开。  一)常量指针。  常量是形容词,指针是名词,以指针为中心的一个偏正结构短语。这样看,常量指针本质是指针,常量修饰它,表示这个指针乃是一个...
  • 数值常量 数值常量就是通常所说的常数。在C++中,数值常量是区分类型的,从字面形式即可识别其类型。 整型常量(整数)的类型 在上一节中已知道:整型数据可分为int, short int,long int以及unsigned int, ...
  • 用中国话的语义分析就可以很方便地把三个概念区分开。一) 常量指针。常量是形容词,指针是名词,以指针为中心的一个偏正结构短语。这样看,常量指针本质是指针,常量修饰它,表示这个指针乃是一个指向常量的指针...
  • 对于字符串String的学习一直是迷迷糊糊,但这块知识却也是重点,要...在自己看了深入理解Java虚拟机这本书后,结合多篇博客,才感觉稍微入门,用这篇博客总结记录一下自己理解下的字符串常量池。 一、常量池 link ...
  • 在Java中,常量池的概念想必很多人都听说过。这也是面试中比较常考的题目之一。在Java有关的面试题中,一般习惯通过String的有关问题来考察面试者对于常量池的知识的理解,几道简单的String面试题难倒了无数的开发者...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 251,459
精华内容 100,583
关键字:

常量分析