文章目录 1.常量 2.Java常量池 2.1. 静态常量池: 2.2. 运行时常量池: 3.基本数据类型包装类常量池 4.java字符串常量池 1.常量 常量表示程序运行过程种不可改变的值,主要作用如下: 1.代表常数,便于程序的重构和修改。 常量池 在java中,为了避免频繁的创建和销毁对象影响系统的性能,引入了常量池,通过常量池实现了对象的共享。 java的常量池可做如下分类: 2.1. 静态常量池: 即*.class文件中的常量池,class文件中的常量池不仅仅包含字符串(数字)字面量,还包含类、方法的信息,占用class文件绝大部分空间。 字符串常量池 在java中,用String表示字符串。
java常量池是一个经久不衰的话题,也是面试官的最爱,题目花样百出,这次好好总结一下。 理论 先拙劣的表达一下jvm虚拟内存分布: ? 虚拟机栈是jvm执行java代码所使用的栈。 方法区存放了一些常量、静态变量、类信息等,可以理解成class文件在内存中的存放位置。 虚拟机堆是jvm执行java代码所使用的堆。 Java中的常量池,实际上分为两种形态:静态常量池和运行时常量池。 而运行时常量池,则是jvm虚拟机在完成类装载操作后,将class文件中的常量池载入到内存中,并保存在方法区中,我们常说的常量池,就是指方法区中的运行时常量池。 前文提到过,class文件中存在一个静态常量池,这个常量池是由编译器生成的,用来存储java源文件中的字面量(本文仅仅关注字面量),假设我们有如下java代码: 1 String s = "hi"; 为了方便起见
腾讯云精选爆款云服务器限时体验6.6元起,还有更多热门云产品满足您的上云需求
Class常量池 什么是Class常量池? " 运行时常量池 什么是运行时常量池? 运行时常量池存在于内存中,是方法区的一部分。它是Class常量池被加载到内存之后的版本。 运行时常量池相对于Class文件常量池的另一个重要特征是具备动态性,Java语言并不要求常量一定只在编译期才能产生,也就是说,并非预置入Class文件中常量池的内容才能进入方法区运行时常量池,运行期间也可以将新的常量池放入池中 字符串常量池 字符串常量池在Java内存区域的哪个位置? 在JDK6.0及之前的版本,字符串常量池是放在Perm Gen区(也就是方法区)中; 在JDK7.0版本,字符串常量被移到了堆中。
Java当中的常量池 在Java虚拟机jvm中,内存分布为:虚拟机堆,程序计数器,本地方法栈,虚拟机栈,方法区。 我们知道在class文件中,有类的版本信息,字段信息,方法,接口等信息,还有一个就是常量池, 这个就是class文件常量池了。 class文件常量池主要用于存放的是什么呢? 常量池是以表的形式存在(表是用来存储字符串值的,不存储符号引用),实际可以分两种,一种为静态常量池,另一种为运行时常量池,共有11中常量表,常量池的每一个常量都代表一张表。 常量池: Class文件中存储所有常量 在Java中说过常量池可以分两种形态,静态常量池和运行时常量池。 运行时常量池是java虚拟机在完成类加载后的操作,将class文件中的常量池加载到内存中,并保证在方法区,我们口中的常量池是在方法区中运行的常量池,运行时常量池具有动态性,在运行期间也能产生新的常量放入池中
Class文件的存在使得不同语言编写的程序都可以运行在Java虚拟机上,只需要这些语言经过编译器编译后的Class文件符合Java虚拟机定义的规范,Java虚拟机就可以加载执行这些Class文件。 常量池 走过了魔数和版本,接下去是Class文件中最关键的部分常量池,常量池由一个计数池和具体的常量项来组成,在我们代码中常量池数量为0x0013(十进制是19),关于常量池的计数池有一个比较特殊的地方就是他是从 1开始计数的,也就是说如果我们计数池的值是19,实际上是只有18个常量项。 常量池中主要存放两大类常量: 字面量 符号引用 字面量主要指的是文本字符串、声明为final的常量值等 符号引用主要包含三类常量: 类和接口的全限定名 字段的名称和描述符 方法的名称和描述符 Java代码在编译的时候不存在连接时 通过比较javap -verbose常量池的项的索引和我们class文件二进制的表示可以发现是一一对应的。
常量池主要用于存放两大类常量:字面量(Literal)和符号引用量(Symbolic References),字面量相当于Java语言层面常量的概念,如文本字符串,声明为final的常量值等,符号引用则属于编译原理方面的概念 运行时常量池相对于CLass文件常量池的另外一个重要特征是具备动态性,Java语言并不要求常量一定只有编译期才能产生,也就是并非预置入CLass文件中常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中 二.8种基本类型的包装类和常量池 1、java中基本类型的包装类的大部分都实现了常量池技术,即Byte,Short,Integer,Long,Character,Boolean; Integer i1 Java中的自动装箱与拆箱 ##三.String类和常量池 1、String对象创建方式 String str1 = "abcd"; String str2 = new String("abcd"); 4、java.lang.String.intern() 运行时常量池相对于CLass文件常量池的另外一个重要特征是具备动态性,Java语言并不要求常量一定只有编译期才能产生,也就是并非预置入CLass文件中常量池的内容才能进入方法区运行时常量池
上一篇Java Class文件格式详解 讲解了整个Class文件的组成部分,因为篇幅的原因没做太多过细的分析,今天我们重点分析下常量池。 上一篇文章讲了常量池总共17(上一篇写的是18,修正一下,因为2没有使用)种类型,我们来分析其中最常用的类型。 前面4字节是CA FE BA BE,这是固定的魔幻数; 接下来4字节是版本号:00 00 00 34 ,换成10进制是52,这是JDK8的版本号; 接下来是00 8C ,这个表示常量池常量项的个数,一共有 0022,再接下来2字节是004C,表示类名索引是34,描述索引是76,那整个常量池的第34和76项是什么呢,可以用Javap看下结果, #1 = Methodref #34.#76 CONSTANT_InterfaceMethodref; 接下来2字节是005C和005D,表示对应的接口名的索引为92,接口方法描述索引为93,因为我们有这么一段代码: pst.addBatch(sql); 92和93索引联合的内容为:java
JVM常量池主要分为Class文件常量池、运行时常量池,全局字符串常量池,以及基本类型包装类对象常量池。 0、Class文件常量池 class文件是一组以字节为单位的二进制数据流,在java代码的编译期间,我们编写的java文件就被编译为.class文件格式的二进制数据存放在磁盘中,其中就包括class文件常量池 运行时常量池相对于class常量池一大特征就是具有动态性,java规范并不要求常量只能在运行时才产生,也就是说运行时常量池的内容并不全部来自class常量池,在运行时可以通过代码生成常量并将其放入运行时常量池中 JDK6 常量池存在持久代,设置了持久代大小后,不断while循环必将撑满 Perm 导致内存溢出;JDK7 常量池被移动到 Native Heap(Java Heap,HotSpot VM中不区分native 3、JAVA 基本类型的封装类及对应常量池 java中基本类型的包装类的大部分都实现了常量池技术,这些类是Byte,Short,Integer,Long,Character,Boolean,另外两种浮点数类型的包装类则没有实现
常量池 是.class文件的常量池,也可以理解为一张表,虚拟机指令根据这张常量表找到要执行的类名,方法名,参数类型,字面量等信息 运行时常量池 常量池是*.class文件中的,当该类被加载,它的常量池信息就会放入运行时常量池 ,并把里面的符号地址变为真实地址 常量池只有类文件在编译的时候才会产生,而且是存储在类文件中的。 而运行时常量池是在方法区,而且可在JVM运行期间动态向运行时常量池中写入数据。 可以看出s3和s5都是对#4常量池的引用,为true的原因是jvm存在编译期优化的机制,在编译期(javac *.java时)会将可以拼接的字符串常量帮你自动拼接了,由于字符串常量池中已经存在了,因此会让 s3=s6输出true 简单介绍intern方法,可以使用intern方法,主动将串池中还没有的字符串对象放入字符串常量池 通过intern方法主动将s4的字符串放入了字符串常量池,将这个字符串对象尝试放入串池
[image-20201017230142565] Class文件常量池 class文件是以字节为单位的二进制数据流,java编译器将java源码文件编译成.class字节码文件存放在磁盘上,.class 中就包含文件常量池(非运行时常量池),在编译期间就确定了,.class文件遵循jvm虚拟机规范. 将字节流的静态存储结构转化成方法区的运行时数据结构 class文件常量池进入运行时常量池,所有类共同使用一个运行时常量池,在进入运行时常量的过程中,多个class常量池中相同的字符串,只会在运行时常量池存在一份 class文件常量池的大部分数据会被加载到运行时常量池。 运行时常量池相比于class文件常量池具有动态性,运行时常量池的内容不全部来自于class文件常量池,可以通过代码生成的方式加入到里面。 字符串常量池 1.字符串创建的两种方式 String rumenz1="入门"; String rumenz2=new String("小站"); 入门在编译期间就已经确定,会进入字符串常量池,但是字符串常量池只会保存一个引用
Pre JVM - 深入剖析字符串常量池 JVM - 基本类型的包装类和对象池 ---- class常量池 Class常量池我们可以理解为是Class文件中的资源仓库。 Class文件中主要由两大部分 类的版本、字段、方法、接口等描述信息外, 常量池(constant pool table),用于存放编译期生成的各种字面量(Literal)和符号引用(Symbolic 这里面主要是两个东西; 字面量和符号引用 ---- 字面量 么错 就是你想的那个意思: 由字母、数字等构成的字符串或者数值常量 。 主要包括了以下三类常量: 类和接口的全限定名 字段的名称和描述符 方法的名称和描述符 刚刚上面的代码 a、b 、c、d就是字段名称,就是一种符号引用, 类的全限定名也是符号引用, 类中的方法名称,()是一种 ---- 运行时常量池 class常量池现在是静态信息,只有到运行时被加载到内存后,这些符号才有对应的内存地址信息,这些常量池一旦被装入内存就变成运行时常量池,对应的符号引用在程序加载或运行时会被转变为被加载到内存区域的代码的直接引用
package com.jvm; import org.junit.Test; /** * 常量池StringTable的详解 * 可以使用该命令查看,当前类的字节码常量池信息 * javap String s2 = "b";//放到常量池中 String s3 = "ab"; //放到常量池中 /** * 首先创建一个StringBuilder 对象 然后从常量池中获取一个"a",然后再从常量池中获取一个"b"对象 * 然后再在堆中创建一个 new String("ab") 对象 * new StringBuilder * 会把 "a"+"b" 优化成 "ab",然后从常量池中查找 */ String s5 = "a" + "b"; //放到常量池中 会把堆中创建的 "ab" 对象放到常量池中,如果常量池中有"ab"了 , * 则不会放到常量池中,但是一定会返回常量池中的对象 * * 对比:test2和test3
* 对比:test2和test3
标题图 Java当中的常量池 在Java虚拟机jvm中,内存分布为:虚拟机堆,程序计数器,本地方法栈,虚拟机栈,方法区。 ? 我们知道在class文件中,有类的版本信息,字段信息,方法,接口等信息,还有一个就是常量池, 这个就是class文件常量池了。 class文件常量池主要用于存放的是什么呢? 常量池是以表的形式存在(表是用来存储字符串值的,不存储符号引用),实际可以分两种,一种为静态常量池,另一种为运行时常量池,共有11中常量表,常量池的每一个常量都代表一张表。 常量池: Class文件中存储所有常量 在Java中说过常量池可以分两种形态,静态常量池和运行时常量池。 运行时常量池是java虚拟机在完成类加载后的操作,将class文件中的常量池加载到内存中,并保证在方法区,我们口中的常量池是在方法区中运行的常量池,运行时常量池具有动态性,在运行期间也能产生新的常量放入池中
这一切都是源于JVM为了减少字符串对象的重复创建,其维护了一个特殊的内存,这段内存被成为字符串常量池或者字符串字面量池。 JVM通过字符串常量池查找不到内容为droid的字符串对象存在,那么会创建这个字符串对象,然后将刚创建的对象的引用放入到字符串常量池中,并且将引用返回给变量str1。 intern 对于上面使用new创建的字符串对象,如果想将这个对象的引用加入到字符串常量池,可以使用intern方法。 字符串常量池实现的前提条件就是Java中String对象是不可变的,这样可以安全保证多个变量共享同一个对象。 字符串常量池存放的是对象引用,不是对象。在Java中,对象都创建在堆内存中。 更新验证,收到的很多评论也在讨论这个问题,我简单的进行了验证。
Class常量池、运行时常量池、字符串常量池 class常量池 java代码经过编译之后都成了xxx.class文件,这是java引以为傲的可移植性的基石。 class文件中,在CAFEBABE、主次版本号之后就是常量池入口了,入口是一个u2类型的数据,也就是占据2个字节,用来给常量池的容量计数,假设这个u2的数字为0x0016,那么对应十进制为22,那么常量池中右 在《Java虚拟机规范8》中是这样描述的,运行时常量池(Runtime constant pool)是class文件中每一个类或者接口的常量池表(constant pool)的运行时表示形式,它包含了若干常量 那么运行时常量池是和类绑定的,每个类、接口有自己的运行时常量池,每一个运行时常量池的内存是在方法区进行分配的,这只是概念上的方法区,每个虚拟机有自己的实现,同一个虚拟机不同的版本也有不同的实现,以常用的 )永久代属于GC heap的一部分 在1.7字符串常量池被从方法区拿到了堆,运行时常量池还留在方法区中 在1.8中hotspot移除了永久代用元空间取代它,字符串常量池还在堆中,而运行时常量池依然在方法区也就是元空间
运行时常量池是方法区(PermGen)的一部分。 需要提前了解: 1. JVM内存模型。 2. JAVA对象在JVM中内存分配 常量池的好处 常量池是为了避免频繁的创建和销毁对象而影响系统性能,其实现了对象的共享。 Java的自动装箱中其实就使用到了运行时常量池。 详见:Java 自动装箱与拆箱的实现原理 还有字符串常量池。 字符串进入到常量池的两种方法: 1. new String()的实例调用intern()方法。 重要提示:虚拟机启动时常量池中就存在“java”字符串实例,下面代码中s2调用intern()方法时,只是返回常量池中“java”实例的引用,而没有添加“java”实例。 JDK1.7-JDK1.8常量池内存模型 ? Paste_Image.png 如图中,我们可以看到常量池位于方法区(PermGen),但常量池引用的字符串实例在堆中。
定义的静态成员 常量池:存放常量 非RAM(随机存取存储器)存储 java内存分配中的栈 在函数中定义的一些基本类型的变量数据和对象的引用变量都在函数的栈内存中分配。 常量池 (constant pool) 常量池指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。 虚拟机必须为每个被装载的类型维护一个常量池。 常量池就是该类型所用到常量的一个有序集和,包括直接常量(string,integer和 floating point常量)和对其他类型,字段和方法的符号引用。 String的 intern()方法就是扩充常量池的 一个方法;当一个String实例str调用intern()方法时,Java 查找常量池中是否有相同Unicode的字符串常量,如果有,则返回其的引用
我回答,“由于字符串的使用频率实在是太高了,所以 Java 虚拟机为了提高性能和减少内存开销,在创建字符串对象的时候进行了一些优化,特意为字符串开辟了一个字符串常量池。” 三妹突然插话到,“有了字符串常量池,就可以通过双引号的方式直接创建字符串对象,不用再通过 new 的方式在堆中创建对象了,对吧?” “是滴。 “那哥,字符串常量池在内存中的什么位置呢?”三妹问。 我说,“三妹,你这个问题问得好呀!” 在 Java 8 之前,字符串常量池在永久代中。 ? Java 8 之后,移除了永久代,字符串常量池就移到了堆中。 ? “哥,能再简单给我解释一下方法区,永久代和元空间的概念吗?有点模糊。”三妹说。 我说,“可以呀。” “那关于字符串常量池,就先说这么多吧,是不是还挺有意思的。”我说。 “是的,我现在是彻底搞懂了字符串常量池,哥,你真棒!”三妹说。
扫码关注腾讯云开发者
领取腾讯云代金券