精华内容
下载资源
问答
  • 在上节关于数据做 == 比较时,提到了字符串常量池,经查询得知常量池既不属于堆,也不属于栈内存 ,那么常量池可能就和方法区有所关系,为此阅读《深入浅出JVM》一书,了解常量池和方法区的关联,同时对于常量池的...

    前言

    Java 的 JVM 的内存可分为 3 个区:堆内存(heap)、栈内存(stack)和方法区(method)也叫静态存储区。

    在学习的过程中经常还会听到常量池这一术语,在上节关于数据做 == 比较时,提到了字符串常量池,经查询得知常量池既不属于堆,也不属于栈内存 ,那么常量池可能就和方法区有所关系,为此阅读《深入浅出JVM》一书,了解常量池和方法区的关联,同时对于常量池的分类也有了一定的认识。

    本文所有代码都是基于 JDK1.8 进行的。

    正文

    在探讨常量池的类型之前需要明白什么是常量。用 final 修饰的成员变量表示常量,值一旦给定就无法改变!

    final 修饰的变量有三种:静态变量、实例变量和局部变量,分别表示三种类型的常量。

    在 Java 的内存分配中,总共 3 种常量池:

    全局字符串池(string pool也有叫做string literal pool)

    字符串常量池在 Java 内存区域的哪个位置在 JDK6.0 及之前版本,字符串常量池是放在 Perm Gen 区(也就是方法区)中,此时常量池中存储的是对象。

    在 JDK7.0 版本,字符串常量池被移到了堆中了。此时常量池存储的就是引用了。在 JDK8.0 中,永久代(方法区)被元空间取代了。

    字符串常量池是什么?

    在 HotSpot VM 里实现的 string pool 功能的是一个 StringTable 类,它是一个 Hash 表,默认值大小长度是1009;里面存的是驻留字符串的引用(而不是驻留字符串实例自身)。也就是说某些普通的字符串实例被这个 StringTable 引用之后就等同被赋予了“驻留字符串”的身份。这个 StringTable 在每个 HotSpot VM 的实例里只有一份,被所有的类共享。

    StringTable 本质上就是个 HashSet。这是个纯运行时的结构,而且是惰性(lazy)维护的。注意它只存储对java.lang.String 实例的引用,而不存储 String 对象的内容。 注意,它只存了引用,根据这个引用可以得到具体的 String 对象。

    在 JDK6.0 中,StringTable 的长度是固定的,长度就是 1009,因此如果放入 String Pool 中的 String 非常多,就会造成 hash 冲突,导致链表过长,当调用 String#intern() 时会需要到链表上一个一个找,从而导致性能大幅度下降;

    在 JDK7.0 中,StringTable 的长度可以通过参数指定:

    -XX:StringTableSize=66666

    class 文件常量池(class constant pool)

    我们都知道,class 文件中除了包含类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池(constant pool table),用于存放编译器生成的各种字面量(Literal)和符号引用(Symbolic References)。 字面量比较接近 Java 语言层面常量的概念,如文本字符串、被声明为 final 的常量值等。 符号引用则属于编译原理方面的概念,包括了如下三种类型的常量:类和接口的全限定名

    字段的名称和描述符

    方法的名称和描述符

    常量池的每一项常量都是一个表,一共有如下表所示的11种各不相同的表结构数据,这每个表开始的第一位都是一个字节的标志位(取值1-12),代表当前这个常量属于哪种常量类型。

    每种不同类型的常量类型具有不同的结构,具体的结构本文就先不叙述了,本文着重区分这三个常量池的概念(读者若想深入了解每种常量类型的数据结构可以查看《深入理解java虚拟机》第六章的内容,其实是自己还没弄明白,后续回来填坑 )。

    运行时常量池(runtime constant pool)

    运行时常量池是方法区的一部分。

    当 Java 文件被编译成 class 文件之后,也就是会生成上面所说的 class 常量池,那么运行时常量池又是什么时候产生的呢?

    JVM 在执行某个类的时候,必须经过加载、连接、初始化,而连接又包括验证、准备、解析(resolve)三个阶段。而当类加载到内存中后,JVM 就会将 class 文件常量池中的内容存放到运行时常量池中,由此可知,运行时常量池也是每个类都有一个。在上面也说了,class 常量池中存的是字面量和符号引用,也就是说它们存的并不是对象的实例,而是对象的符号引用值。而经过resolve 之后,也就是把符号引用替换为直接引用,解析的过程会去查询全局字符串池,也就是上面所说的 StringTable,以保证运行时常量池所引用的字符串与全局字符串池中所引用的是一致的。

    三种常量池之间的关联

    关于 JVM 执行的时候,还涉及到了字符串常量池。

    在类加载阶段, JVM 会在堆中创建对应这些 class 文件常量池中的字符串对象实例,并在字符串常量池中驻留其引用。具体在 resolve 阶段执行。这些常量全局共享。

    这里说的比较笼统,没错,是 resolve 阶段,但是并不是大家想的那样,立即就创建对象并且在字符串常量池中驻留了引用。 JVM 规范里明确指定 resolve 阶段可以是 lazy 的。

    JVM 规范里 Class 文件常量池项的类型,有两种东西:CONSTANT_Utf8 和CONSTANT_String。前者是 UTF-8 编码的字符串类型,后者是 String 常量的类型,但它并不直接持有 String 常量的内容,而是只持有一个 index,这个 index 所指定的另一个常量池项必须是一个 CONSTANT_Utf8 类型的常量,这里才真正持有字符串的内容。

    在HotSpot VM中,运行时常量池里,

    CONSTANT_Utf8 -> Symbol*(一个指针,指向一个Symbol类型的C++对象,内容是跟Class文件同样格式的UTF-8编码的字符串)

    CONSTANT_String -> java.lang.String(一个实际的Java对象的引用,C++类型是oop)

    CONSTANT_Utf8 会在类加载的过程中就全部创建出来,而 CONSTANT_String 则是 lazy resolve 的,例如说在第一次引用该项的 ldc 指令被第一次执行到的时候才会 resolve。那么在尚未 resolve 的时候,HotSpot VM 把它的类型叫做JVM_CONSTANT_UnresolvedString,内容跟 Class 文件里一样只是一个 index;等到 resolve 过后这个项的常量类型就会变成最终的 JVM_CONSTANT_String,而内容则变成实际的那个 oop。

    看到这里想必也就明白了, 就 HotSpot VM 的实现来说,加载类的时候,那些字符串字面量会进入到当前类的运行时常量池,不会进入全局的字符串常量池(即在 StringTable 中并没有相应的引用,在堆中也没有对应的对象产生)。所以上面提到的,经过 resolve 时,会去查询全局字符串池,最后把符号引用替换为直接引用。(即字面量和符号引用虽然在类加载的时候就存入到运行时常量池,但是对于 lazy resolve 的字面量,具体操作还是会在 resolve 之后进行的。)

    关于 lazy resolution 需要在这里了解一下 ldc 指令

    简单地说,它用于将 String 型常量值从常量池中推送至栈顶。

    以下面代码为例:

    public static void main(String[] args) {

    String s = "abc";

    }

    比如说该代码文件为 Test.java,首先在文件目录下打开 Dos 窗口,执行 javac Test.java 进行编译,然后输入 javap -verbose Test 查看其编译后的 class 文件如下:

    使用 ldc 指令将"abc"加载到操作数栈顶,然后用 astore_1 把它赋值给我们定义的局部变量 s,然后 return。

    结合上文所讲,在 resolve 阶段( constant pool resolution ),字符串字面量被创建对象并在字符串常量池中驻留其引用,但是这个 resolve 是 lazy 的。换句话说并没有真正的对象,字符串常量池里自然也没有,那么 ldc 指令还怎么把值推送至栈顶并进行了赋值操作?或者换一个角度想,既然 resolve 阶段是 lazy 的,那总有一个时候它要真正的执行吧,是什么时候?

    执行 ldc 指令就是触发 lazy resolution 动作的条件

    ldc 字节码在这里的执行语义是:到当前类的运行时常量池(runtime constant pool,HotSpot VM里是ConstantPool + ConstantPoolCache)去查找该 index 对应的项,如果该项尚未 resolve 则 resolve 之,并返回 resolve 后的内容。 在遇到 String 类型常量时,resolve 的过程如果发现 StringTable 已经有了内容匹配的 java.lang.String 的引用,则直接返回这个引用;反之,如果 StringTable 里尚未有内容匹配的 String 实例的引用,则会在 Java 堆里创建一个对应内容的 String 对象,然后在 StringTable 记录下这个引用,并返回这个引用。

    可见,ldc 指令是否需要创建新的 String 实例,全看在第一次执行这一条 ldc 指令时,StringTable 是否已经记录了一个对应内容的 String 的引用。

    用以下代码做分析展示:

    public static void main(String[] args) {

    String s1 = "abc";

    String s2 = "abc";

    String s3 = "xxx";

    }

    查看其编译后的 class 文件如下:

    用图解的方式展示:

    String s1 = "abc";resolve 过程在字符串常量池中发现没有”abc“的引用,便在堆中新建一个”abc“的对象,并将该对象的引用存入到字符串常量池中,然后把这个引用返回给 s1。

    String s2 = "abc"; resolve 过程会发现 StringTable 中已经有了”abc“对象的引用,则直接返回该引用给 s2,并不会创建任何对象。

    String s3 = "xxx"; 同第一行代码一样,在堆中创建对象,并将该对象的引用存入到 StringTable,最后返回引用给 s3。

    常量池与 intern 方法

    public static void main(String[] args) {

    String s1 = "ab";//#1

    String s2 = new String(s1+"d");//#2

    s2.intern();//#3

    String s4 = "xxx";//#4

    String s3 = "abd";//#5

    System.out.println(s2 == s3);//true

    }

    查看其编译后的 class 文件如下:

    通过 class 文件信息可知,“ab”、“d”、“xxx”,“abd”进入到了 class 文件常量池,由于类在 resolve 阶段是 lazy 的,所以是不会创建实例对象,更不会驻留字符串常量池。

    图解如下:

    进入 main 方法,对每行代码进行解读。1,ldc 指令会把“ab”加载到栈顶,换句话说,在堆中创建“ab”对象,并把该对象的引用保存到字符串常量池中。

    2,ldc 指令会把“d”加载到栈顶,然后有个拼接操作,内部是创建了一个 StringBuilder 对象,一路 append,最后调用 StringBuilder 对象的 toString 方法得到一个 String 对象(内容是 abd,注意 toString 方法会 new 一个 String 对象),并把它赋值给 s2(赋值给 s2 的依然是对象的引用而已)。注意此时没有把“abd”对象的引用放入字符串常量池。

    3,intern 方法首先会去字符串常量池中查找是否有“abd”对象的引用,如果没有,则把堆中“abd”对象的引用保存到字符串常量池中,并返回该引用,但是我们并没有使用变量去接收它。

    4,无意义,只是为了说明 class 文件中的“abd”字面量是#5时得到的。

    5,字符串常量池中已经有“abd”对象的引用,因此直接将该引用返回给 s3。

    总结

    1、全局字符串常量池在每个 VM 中只有一份,存放的是字符串常量的引用值。

    2、class 常量池是在编译的时候每个 class 都有的,在编译阶段,存放各种字面量和符号引用。

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

    4、class 文件常量池中的字符串字面量在类加载时进入到运行时常量池,在真正在 resolve 阶段(即执行 ldc 指令时)时将该字符串的引用存入到字符串常量池中,另外运行时常量池相对于 class 文件常量池具备动态性,有些常量不一定在编译期产生,也就是并非预置入 class 文件常量池的内容才能进入到方法区运行时常量池,运行期间通过 intern 方法,将字符串常量存入到字符串常量池中和运行时常量池(关于优先进入到哪个常量池,私以为先进入到字符串常量池,具体实现还望大神指教)。

    参考链接

    展开全文
  • 方法区和常量池

    2021-02-28 14:39:29
    1.方法区里存class文件信息class文件常量池是个什么关系。2.class文件常量池和运行时常量池是什么关系。方法区存着类的信息,常量静态变量,即类被编译后的数据。这个说法其实是没问题的,只是太笼统了。更加...

    最近一直被方法区里面存着什么东西困扰着?

    1.方法区里存class文件信息和class文件常量池是个什么关系。

    2.class文件常量池和运行时常量池是什么关系。

    方法区存着类的信息,常量和静态变量,即类被编译后的数据。这个说法其实是没问题的,只是太笼统了。更加详细一点的说法是方法区里存放着类的版本,字段,方法,接口和常量池。常量池里存储着字面量和符号引用。

    符号引用包括:1.类的全限定名,2.字段名和属性,3.方法名和属性。

    下面一张图是我画的方法区,class文件信息,class文件常量池和运行时常量池的关系

    bf33b18bb90ec101943fff97f9b7fcf8.png

    下面一张图用来表示方法区class文件信息包括哪些内容:

    b57f5fc12df1da936f14809bf3daf226.png

    可以看到在方法区里的class文件信息包括:魔数,版本号,常量池,类,父类和接口数组,字段,方法等信息,其实类里面又包括字段和方法的信息。

    下面的图表是class文件中存储的数据类型

    类型

    名称

    数量

    u4

    magic

    1

    u2

    minor_version

    1

    u2

    major_version

    1

    u2

    constant_pool_count

    1

    cp_info

    constant_pool

    constant_pool_count - 1

    u2

    access_flags

    1

    u2

    this_class

    1

    u2

    super_class

    1

    u2

    interfaces_count

    1

    u2

    interfaces

    interfaces_count

    u2

    fields_count

    1

    field_info

    fields

    fields_count

    u2

    methods_count

    1

    method_info

    methods

    methods_count

    u2

    attribute_count

    1

    attribute_info

    attributes

    attributes_count

    下面用一张图来表示常量池里存储的内容:

    170a002419c7113fbd128d250787b748.png

    用一个class文件实际反编译一下

    下面是原java代码

    public class TestInt {

    private String str = "hello";

    void printInt(){

    System.out.println(65535);

    }

    }

    经过反编译后获得class文件是下面这样的  javap -v -verbose xxx.class

    1a4055270788b220c534c16d015d8b40.png

    可以看出被反编译的class文件中的内容和上面所说的是能对应上的。这就解答了class文件和静态常量池(class文件常量池)的关系

    静态常量池和动态常量池的关系以及区别

    静态常量池存储的是当class文件被java虚拟机加载进来后存放在方法区的一些字面量和符号引用,字面量包括字符串,基本类型的常量,符号引用其实引用的就是常量池里面的字符串,但符号引用不是直接存储字符串,而是存储字符串在常量池里的索引。

    动态常量池是当class文件被加载完成后,java虚拟机会将静态常量池里的内容转移到动态常量池里,在静态常量池的符号引用有一部分是会被转变为直接引用的,比如说类的静态方法或私有方法,实例构造方法,父类方法,这是因为这些方法不能被重写其他版本,所以能在加载的时候就可以将符号引用转变为直接引用,而其他的一些方法是在这个方法被第一次调用的时候才会将符号引用转变为直接引用的。

    总结:

    方法区里存储着class文件的信息和动态常量池,class文件的信息包括类信息和静态常量池。可以将类的信息是对class文件内容的一个框架,里面具体的内容通过常量池来存储。

    动态常量池里的内容除了是静态常量池里的内容外,还将静态常量池里的符号引用转变为直接引用,而且动态常量池里的内容是能动态添加的。例如调用String的intern方法就能将string的值添加到String常量池中,这里String常量池是包含在动态常量池里的,但在jdk1.8后,将String常量池放到了堆中。

    下面有一篇文章写的是比较好的

    http://blog.csdn.net/vegetable_bird_001/article/details/51278339

    https://www.cnblogs.com/holos/p/6603379.html

    展开全文
  • java8内存结构图虚拟机内存与本地内存的区别Java虚拟机在执行的时候会把管理的内存分配成不同的区域,这些区域被称为虚拟机内存,同时,对于虚拟机没有直接管理的物理内存,也有一定的利用,这些被利用却不在虚拟机...

    java8内存结构介绍

    java虚拟机在jdk8改变了许多,网络上各种解释都有,在查阅了官方文档以及一下大佬的解释以后,我来粗浅的介绍一下我理解的java8的内存结构。

    java8内存结构图

    虚拟机内存与本地内存的区别

    Java虚拟机在执行的时候会把管理的内存分配成不同的区域,这些区域被称为虚拟机内存,同时,对于虚拟机没有直接管理的物理内存,也有一定的利用,这些被利用却不在虚拟机内存数据区的内存,我们称它为本地内存,这两种内存有一定的区别:

    JVM内存受虚拟机内存大小的参数控制,当大小超过参数设置的大小时就会报OOM

    本地内存本地内存不受虚拟机内存参数的限制,只受物理内存容量的限制虽然不受参数的限制,但是如果内存的占用超出物理内存的大小,同样也会报OOM

    java运行时数据区域

    java虚拟机在执行过程中会将所管理的内存划分为不同的区域,有的随着线程产生和消失,有的随着java进程产生和消失,根据《Java虚拟机规范》的规定,运行时数据区分为以下一个区域:

    程序计数器(Program Counter Register)

    程序计数器就是当前线程所执行的字节码的行号指示器,通过改变计数器的值,来选取下一行指令,通过它来实现跳转、循环、恢复线程等功能。

    在任何时刻,一个处理器内核只能运行一个线程,多线程是通过线程轮流切换,分配时间来完成的,这就需要有一个标志来记住每个线程执行到了哪里,这里便需要到了程序计数器。

    所以,程序计数器是线程私有的,每个线程都已自己的程序计数器。

    虚拟机栈(JVM Stacks)

    虚拟机栈是线程私有的,随线程生灭。虚拟机栈描述的是线程中的方法的内存模型:

    每个方法被执行的时候,都会在虚拟机栈中同步创建一个栈帧(stack frame)

    每个栈帧的包含如下的内容局部变量表局部变量表中存储着方法里的java基本数据类型(byte/boolean/char/int/long/double/float/short)以及对象的引用(注:这里的基本数据类型指的是方法内的局部变量)操作数栈动态连接方法返回地址

    方法被执行时入栈,执行完后出栈

    虚拟机栈可能会抛出两种异常:

    如果线程请求的栈深度大于虚拟机所规定的栈深度,则会抛出StackOverFlowError即栈溢出

    如果虚拟机的栈容量可以动态扩展,那么当虚拟机栈申请不到内存时会抛出OutOfMemoryError即OOM内存溢出

    本地方法栈(Native Method Stacks)

    本地方法栈与虚拟机栈的作用是相似的,都会抛出OutOfMemoryError和StackOverFlowError,都是线程私有的,主要的区别在于:

    虚拟机栈执行的是java方法

    本地方法栈执行的是native方法(什么是Native方法?)

    Java堆(Java Heap)

    java堆是JVM内存中最大的一块,由所有线程共享,是由垃圾收集器管理的内存区域,主要存放对象实例,当然由于java虚拟机的发展,堆中也多了许多东西,现在主要有:

    对象实例类初始化生成的对象基本数据类型的数组也是对象实例

    字符串常量池字符串常量池原本存放于方法区,jdk7开始放置于堆中。字符串常量池存储的是string对象的直接引用,而不是直接存放的对象,是一张string table

    静态变量静态变量是有static修饰的变量,jdk7时从方法区迁移至堆中

    线程分配缓冲区(Thread Local Allocation Buffer)线程私有,但是不影响java堆的共性增加线程分配缓冲区是为了提升对象分配时的效率

    java堆既可以是固定大小的,也可以是可扩展的(通过参数-Xmx和-Xms设定),如果堆无法扩展或者无法分配内存时也会报OOM

    方法区(Method Area)

    方法区绝对是网上所有关于java内存结构文章争论的焦点,因为方法区的实现在java8做了一次大革新,现在我们来讨论一下:

    方法区是所有线程共享的内存,在java8以前是放在JVM内存中的,由永久代实现,受JVM内存大小参数的限制,在java8中移除了永久代的内容,方法区由元空间(Meta Space)实现,并直接放到了本地内存中,不受JVM参数的限制(当然,如果物理内存被占满了,方法区也会报OOM),并且将原来放在方法区的字符串常量池和静态变量都转移到了Java堆中,方法区与其他区域不同的地方在于,方法区在编译期间和类加载完成后的内容有少许不同,不过总的来说分为这两部分:

    类元信息(Klass)类元信息在类编译期间放入方法区,里面放置了类的基本信息,包括类的版本、字段、方法、接口以及常量池表(Constant Pool Table)常量池表(Constant Pool Table)存储了类在编译期间生成的字面量、符号引用(什么是字面量?什么是符号引用?),这些信息在类加载完后会被解析到运行时常量池中

    运行时常量池(Runtime Constant Pool)运行时常量池主要存放在类加载后被解析的字面量与符号引用,但不止这些运行时常量池具备动态性,可以添加数据,比较多的使用就是String类的intern()方法

    直接内存

    直接内存位于本地内存,不属于JVM内存,但是也会在物理内存耗尽的时候报OOM,所以也讲一下。

    在jdk1.4中加入了NIO(New Input/Putput)类,引入了一种基于通道(channel)与缓冲区(buffer)的新IO方式,它可以使用native函数直接分配对外内存,然后通过存储在java堆中的DirectByteBuffer对象作为这块内存的引用进行操作,这样可以在一些场景下大大提高IO性能,避免了在java堆和native堆来回复制数据。

    常见问题 什么是Native方法?

    由于java是一门高级语言,离硬件底层比较远,有时候无法操作底层的资源,于是,java添加了native关键字,被native关键字修饰的方法可以用其他语言重写,这样,我们就可以写一个本地方法,然后用C语言重写,这样来操作底层资源。当然,使用了native方法会导致系统的可移植性不高,这是需要注意的。

    成员变量、局部变量、类变量分别存储在内存的什么地方?

    类变量类变量是用static修饰符修饰,定义在方法外的变量,随着java进程产生和销毁在java8之前把静态变量存放于方法区,在java8时存放在堆中

    成员变量成员变量是定义在类中,但是没有static修饰符修饰的变量,随着类的实例产生和销毁,是类实例的一部分由于是实例的一部分,在类初始化的时候,从运行时常量池取出直接引用或者值,与初始化的对象一起放入堆中

    局部变量局部变量是定义在类的方法中的变量在所在方法被调用时放入虚拟机栈的栈帧中,方法执行结束后从虚拟机栈中弹出,所以存放在虚拟机栈中

    由final修饰的常量存放在哪里?

    final关键字并不影响在内存中的位置,具体位置请参考上一问题。

    类常量池、运行时常量池、字符串常量池有什么关系?有什么区别?

    类常量池与运行时常量池都存储在方法区,而字符串常量池在jdk7时就已经从方法区迁移到了java堆中。

    在类编译过程中,会把类元信息放到方法区,类元信息的其中一部分便是类常量池,主要存放字面量和符号引用,而字面量的一部分便是文本字符,在类加载时将字面量和符号引用解析为直接引用存储在运行时常量池;对于文本字符来说,它们会在解析时查找字符串常量池,查出这个文本字符对应的字符串对象的直接引用,将直接引用存储在运行时常量池;字符串常量池存储的是字符串对象的引用,而不是字符串本身。

    什么是字面量?什么是符号引用?

    字面量java代码在编译过程中是无法构建引用的,字面量就是在编译时对于数据的一种表示:int a=1;//这个1便是字面量 String b="iloveu";//iloveu便是字面量 12

    符号引用由于在编译过程中并不知道每个类的地址,因为可能这个类还没有加载,所以如果你在一个类中引用了另一个类,那么你完全无法知道他的内存地址,那怎么办,我们只能用他的类名作为符号引用,在类加载完后用这个符号引用去获取他的内存地址。例子:我在com.demo.Solution类中引用了com.test.Quest,那么我会把com.test.Quest作为符号引用存到类常量池,等类加载完后,拿着这个引用去方法区找这个类的内存地址。

    展开全文
  • 1. 方法区:存放全局变量静态变量2.常量池:存放常量字符串3.栈:存放函数的参数值,局部变量的值4.堆:存放成员变量(实例变量),一般内存泄漏会发生堆:存放所有new 出来的对象栈:存放基本类型的变量数据对象...

    程序中存放数据的内存分为四块,另有一块存放代码。

    1. 方法区:存放全局变量和静态变量

    2.常量池:存放常量字符串

    3.栈:存放函数的参数值,局部变量的值

    4.堆:存放成员变量(实例变量),一般内存泄漏会发生

    堆:存放所有new 出来的对象

    栈:存放基本类型的变量数据和对象的引用,对象(new 出来的对象)本身并不存在栈中,而是存放在堆中或者常量池中(字符串常量对象)

    常量池:存放基本类型常量和字符串常量

    对于栈和常量池中的对象可以共享,对于堆中的对象不可以共享。栈中的数据大小和生命周期是可以确定的,当没有引用指向数据时,这个数据就会消失。堆中的对象的垃圾回事器负责回收,因此大小和声明周期不需要确定,具有很大的灵活性

    对于字符串来说,其对象的引用都是存储在栈中的, 如果是编译期已经创建好的(用双引号定义的)就存储在常量池中,如果是运行期(new 出来的对象) 则存储在堆中。对于equals相等的字符串,在常量池中也只有一份,在堆中有多份。

    java的堆是一个运行时数据区,类的(对象从中分配空间,堆由垃圾回收机制来负责的,堆的优势就是可以动态的分配内存大小,生存期不必实现告诉编译器,因为他是在运行时动态分配内存的,java的垃圾收集器会自动收走这些不再使用的数据,但缺点是由于要在运行时动态分配内存,存取速比较慢)

    栈:存取速度比寄存器快,栈数据可以共享,但缺点是 栈中的数据大小和生存期必须是确定的,缺乏灵活性。栈中存放的是一些基本类型的变量(int short byte long float double boolean char )和对象把柄。

    展开全文
  • 栈(stack):主要保存基本类型(或者叫内置类型)(char、...堆(heap):用于存储对象数组常量池常量池的划分Class 文件常量池运行时常量池字符串常量池1. Class 文件常量池Class 文件常量池指的是编译生成的 class ...
  • Java中的常量池,实际上分为两种形态:静态常量池和运行时常量池。1)所谓静态常量池,即*.class文件中的常量池,class文件中的常量池不仅仅包含字符串(数字)字面量,还包含类、方法的信息,占用class文件绝大部分...
  • 版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接本声明。本文链接:https://blog.csdn.net/qq_34490018/article/details/82110578目录JVM相关知识String源码分析Srtring在JVM...
  • 运行时常量池方法区的一部分,方法区用于存放Class的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。String.intern()是一个native方法,它的作用是:如果字符串常量池中已经包含了一个等于此String...
  • 作者:lei6393blog.csdn.net/qq_35621494/article/details/107351237java8内存结构图虚拟机内存与本地内存的区别Java虚拟机在执行...
  • 栈:存放基本类型的变量数据对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)或者常量池中(字符串常量对象存放在常量池中。)3. 堆:存放所有new出来的对象。4. 静态域:存放静态成员(static...
  • Java中常量以及常量池

    2021-02-28 15:50:16
    1、举例说明 变量 常量 字面量1 int a=10;2 float b=1.234f;...2、常量池常量池专门用来用来存放常量的内存区域,常量池分为:静态常量池和运行时常量池;静态常量池:*.class文件中的常量池,class文件中的...
  • 1、Java内存模型Java虚拟机在执行程序时把它管理的内存分为若干数据区域,这些数据区域分布情况如下图...Java虚拟机栈:线程私有的,其生命周期线程一致,每个方法执行时都会创建一个栈帧用于存储局部变量表、操作...
  • 你知道的越多,不知道的就越多,业余的像一棵小草!你来,我们一起精进!你不来,我你的竞争对手一起精进!编辑:业余草blog.csdn.net/qq_35621494推荐:https://w...
  • 1:栈在函数中定义的一些基本类型的变量数据对象的引用...每个线程包含一个栈,每个栈中的数据(原始类型对象引用)都是私有的,其他栈不能访问。栈分为3个部分:基本类型变量、执行环境上下文、操作指令(存...
  • 深入理解JVM内存分配和常量池

    千次阅读 2021-01-14 15:02:41
    运行时数据组成和各个区域的作用我们看到运行时数据可以分为线程共享和线程不共享两部分,其中堆内存和方法区线程共享,本地方法栈、虚拟机栈、程序计数器线程不共享。接下来我们介绍每一个区域的作用:2.1.程序...
  • java的内存分配中,经常听到很多关于常量池的描述,我开始看的时候也是看的很模糊,网上五花八门的说法简直太多了,最后查阅各种资料,终于算是差不多理清了,很多网上说法都有问题,笔者尝试着来区分一下这几个...
  • Class文件(也就是.java文件编译后生成的字节码.class文件)中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量符号引用()。这部分内容将...
  • 运行时常量池方法区的一部分,方法区用于存放Class的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。String.intern()是一个native方法,它的作用是:如果字符串常量池中已经包含了一个等于此String...
  • 本文将介绍 HotSpot 中的 String Pool,字符串常量池。相对是一篇比较简单的文章,大家花几分钟就看完了。在 Java 世界中,构造一个 Java 对象是一个相对比较重的活,而且还需要垃圾回收,而缓存池就是为了缓解这个...
  • 分区规范首先要明确,JVM规范中并没有常量池这...下面的常量池主要以Java8自带的HotSpot为例,其他版本的Jvm会有各种区别。在HotSpot中,JDK6之前的版本所有常量池都在永生代(permanent generation)中,而JDK8取消了...
  • 简介: 这几天在看Java虚拟机方面的知识时,看到了有几种不同常量池的说法,然后我就去CSDN、博客园等...在JDK6.0及之前版本,字符串常量池是放在Perm Gen(也就是方法区)中; 在JDK7.0版本,字符串常量池被移到了堆
  • 方法区线程共享。当JVM使用类装载器装载某个类时,首先获取class文件,提取该文件的内容信息,将这些信息存储到方法区,最后...GC在这区域较少出现,内存回收的主要目标是针对常量池的回收对类的卸载(某个类不再使...
  • 本文为joshua317原创文章,转载请注明:转载自joshua317博客Java对象在内存中的表现形式,栈、堆、方法区常量池 - joshua317的博客 Java内存分配与管理是Java的核心技术之一,不管学习任何一门语言,我们要知...
  • 方法区 方法区是 JVM 规范中提出一个概念,不同的 Java 虚拟机都有自己的方法区,但是对方法区的具体实现...到了 JDK 7 开始逐步废弃永久代,首先将永久代中的常量、静态变量、字符串常量池移动到了 Java 堆中。 到了
  • 1、JavaJava在函数中定义的基本类型(int,long,short,byte,float,double,boolean,char)的变量(局部变量函数的形参)的引用数据,以及对象的引用都放在栈中存储。1、栈的特点1、存取速度快。仅次于CPU中的寄存器...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 153,995
精华内容 61,598
关键字:

java方法区和常量池

java 订阅