精华内容
下载资源
问答
  • Java常量池方法区

    2020-01-11 20:29:53
    JDK1.8后方法区内存结构的变化 2、添加链接描述 3、添加链接描述 4、添加链接描述 从字节码分析了JDK7中常量放到中后的 内存结构 1、运行时数据区域 Java虚拟机执行Java程序的过程中会把它所管理的内存划分为...

    转载自:1、Java内存区域介绍(附带JDK1.8后方法区的变化) Java内存
    区域介绍(附带JDK1.8后方法区内存结构的变化
    2、深度解析java.String.intern()及其带来的问题
    4、java.String.intern() 从字节码分析了JDK7中常量池放到堆中后的
    内存结构
    5、java常量池技术

    1、运行时数据区域

    Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。根据《Java虚拟机规范(JavaSE7版)》的规定,java虚拟机所管理的内存将会包括以下几个运行时数据区域:
    在这里插入图片描述
    ##1.程序计数器(Program counter Register)
    1.程序计数器是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器(字节码文件:Hello.class)。在虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条要执行的字节码指令、分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。
    2.由于由于java虚拟机的多线程是通过线程轮流切换并分配处理器时间片的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间的计数器互不影响,独立存储,称这类内存区域为“线程私有内存”。
    3.如果线程正在执行的是一个java方法,这个计数器记录的是正在执行的虚拟机字节码文件中指令的地址;如果正在执行的是Native方法,这个计数器值则为空(Undefined)(本地方法不使用字节码文件?)。此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。(只是一个行号指示器,要么有值要么为空,肯定不会溢出)
    ##2.Java虚拟机栈
    1.与程序计数器一样,Java虚拟机栈(Java Virtual Machine Stacks)也是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:
    在这里插入图片描述
    每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)(栈帧以一个函数|方法为单位,包括main入口函数)用于存储函数局部变量表、操作数栈、动态链接、方法出口(return到的栈帧地址)等信息。每个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
    2.局部变量表存放了编译期可知的各种基本数据类型(Boolean,byte,char,short,int,float,long,double)、对象引用(reference类型,它不等同于对象本身,根据不同的虚拟机实现,它可能是一个指向对象起始地址的引用指针,也可能指向一个代表对象的句柄(二级指针)或者其他与此对象相关的位置)和返回地址类型
    3.其中64位长的long和double类型的数据会占用2个局部变量空间(Slot),其余的数据类型只占用一个(说明Java栈帧与C++栈帧相同,都是以4字节为单位,要么直接是局部变量值,要么是引用指针),局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在栈帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小。
    4.如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverFlowError异常;如果虚拟机栈可以动态扩展(当前大部分Java虚拟机都可动态扩展,只不过Java虚拟机规范中也允许固定长度的虚拟机栈),当扩展无法申请到足够的内存时会抛出OutOfMemoryError异常。
    ##3.本地方法栈
    本地方法栈和虚拟机栈发挥的作用十分相似。同样是线程私有,他们之间的区别不过是虚拟机栈为Java方法函数服务,而本地方法栈为虚拟机使用到的Native方法服务。在HotSpot虚拟机中直接就把本地方法栈和虚拟机栈合二为一。与虚拟机栈一样,本地方法栈也会抛出StackOverFlowError异常和OutOfMemoryError异常。
    ##4.Java堆
    1.对于大多数应用来说,Java堆(Java Heap)是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。这一点在Java虚拟机规范中描述的是:所有的对象实例以及数组都要在堆上分配。但是随着JIT编译器(Just in Time即时编译器)的发展与逃逸分析技术(通过分析若一个对象没有逃逸出一个方法,那么该对象在栈中分配空间,该对象随着栈的销毁而销毁)的逐渐成熟,栈上分配、标量替换优化技术(将部分字段使用标量存储)将会导致一些微妙的变化发生,所有的对象都分配在堆上也逐渐显得不是那么“绝对”了。
    2.Java堆是垃圾收集器管理的主要区域,因此很多时候也被称为“GC堆”(Garbage Collected Heap),如果从内存回收的角度看,由于现在收集器基本都是采用的分代收集算法,所以Java堆中还可以细分为:新生代和老年带;更细致一点的有Eden空间(伊甸空间)、From Survivor空间(上次内存整理存活下来的空间?)、To Survivor(本次内存整理将要存活下去的空间)等。如果从内存分配的角度看,线程共享的Java堆中可能划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer,TLAB)。不过,无论如何划分,都与存放内容无关,无论哪个区域,存储的都仍然是对象实例,进一步划分的目的是为了更好地回收内存,或者更快的分配内存。
    3.根据Java虚拟机规范的规定,Java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可(当堆中不连续的空间越来越多,可能会发生明明剩余空间大于需要申请的空间却申请失败,for what?),就像我们的磁盘空间一样。在实现时,既可以实现成固定大小的,也可以是可扩展的,不过当前主流的虚拟机都是按照可扩展来实现的,如果在堆中没有内存能完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。
    ##5.方法区
    方法区(Method Area)与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量(方法可以有-局部常量应该是存储在栈帧中的,类也可以有但没必要跟随对象存储在堆中所以是存储在方法区)、静态变量(方法可以有-即局部静态变量,类也可以有-即类变量)、即时编译器编译后的代码等数据。虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫作Non-Heap非堆,目的应该是与Java Heap区分开来。
    ###5.1.方法区存储的类信息
    对每个加载的类型(类class、接口interface、枚举enum、注解annotation),JVM必须在方法区中存储以下类型信息:
    。这个类型的完整有效名称(全名=包名.类名)
    。这个类型直接父类的完整有效名称(java.lang.object除外,其他类型若没有声明父类,默认父类是Object)
    。这个类型的修饰符(public、abstract、final的某个子集)
    。这个类型直接接口的一个有序列表
    除此之外方法区存储的类信息还有:
    。类型的常量池(constant pool JDK8后常量池放在堆中)
    。域(Field)信息
    。方法(Method)信息
    。除了常量外的所有静态(static)变量
    ###5.2.方法区存储的常量
    类的static final修饰的成员变量都存储于方法区
    ###5.3.方法区存储的静态变量
    。静态变量又称为类变量,类中被static修饰的成员变量都是静态变量(类变量)
    。静态变量之所以又称为类变量,是因为静态变量和类关联在一起,随着类的加载而存在于方法区(而不是堆中),在单例模式中可以在类中定义一个类的静态实例,其也是随着类的加载而存在于方法区中。
    在这里插入图片描述
    注:在JDK6以及以前的版本中,字符串的常量池是放在堆的Perm区的,Perm区是一个类静态的区域,主要存储一些加载类的信息、常量池、方法片段等内容,默认大小只有4MB,一旦常量池中大量使用intern是会直接产生java.lang.OutOfMemoryError: PermGen space错误的。所以在JDK7的版本中,字符串常量池已经从Perm取一道正常的Java Heap区域了。
    在这里插入图片描述

    ##6.运行时常量池
    1.运行时常量池(Runtime Constant Pool)是方法区的一部分(虚拟机规范)。Class文件信息中除了有类的版本、字段(类名)、方法、接口等描述信息外,还有一项信息是class文件常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用这部分内容将在类加载后存放到方法区的运行时常量池中
    2.运行时常量池相对于Class文件常量池的另外一个重要特征是具备动态性,Java语言并不要求

    展开全文
  • 我们知道JDK1.8中取消了永久代,而代之使用了元空间来实现方法区。话虽如此,但是关于字符串常量池和运行时常量池的模棱两可的说法一直都是争论不休的。 1)方法区包含哪些内容? 方法区包含哪些内容,摘录自...


            我们知道在JDK1.8中取消了永久代,区而代之使用了元空间来实现方法区。话虽如此,但是关于字符串常量池和运行时常量池的模棱两可的说法一直都是争论不休的。

    1)方法区包含哪些内容?

    方法区包含哪些内容,摘录自《java虚拟机规范-第8版》:
    在这里插入图片描述
    方法区包含:
            运行时常量池
            自动和方法数据
            构造函数和普通方法的字节码内容
            一些特殊方法
    这里虽然没有说明“字符串常量池”,但是它也是方法区的一部分。

    2)运行时常量池存在什么地方?

    下面是《深入理解Java虚拟机》一段摘录:
    在这里插入图片描述
    能够看到
            运行时常量池是在方法区中
            对于运行时常量池,《Java虚拟机规范》并没有做任何细节的要求,不同提供商实现的虚拟机可以按照自己的需要来实现这个内存区域

    3)取消永久代后,方法区的实现?

    在这里插入图片描述

    取消永久代后,使用元空间来实现方法区。
            在JDK1.8中,把JDK 7中永久代还剩余的内容(主要是类型信息)全部移到元空间中。注意这里的剩余内容:说明原来移除从永久代移出的字符串常量池,静态常量,在更换了方法区实现后,并没有顺势进入到元空间,那么它们到哪里去了呢?

    4)字符串常量池和运行时常量池究竟去了哪里?

            在JDK1.8中,使用元空间代替永久代来实现方法区,但是方法区并没有改变,所谓"Your father will always be your father",变动的只是方法区中内容的物理存放位置。正如上面所说,类型信息(元数据信息)等其他信息被移动到了元空间中;但是运行时常量池和字符串常量池被移动到了堆中。但是不论它们物理上如何存放,逻辑上还是属于方法区的。

            JDK1.8中字符串常量池和运行时常量池逻辑上属于方法区,但是实际存放在堆内存中,因此既可以说两者存放在堆中,也可以说两则存在于方法区中,这就是造成误解的地方。

            关于佐证运行常量池和字符串常量池被移动到了堆中,可以参考这个博客:https://mp.weixin.qq.com/s__biz=MzI4NDY5Mjc1Mg==&mid=2247485613&idx=1&sn=b2b1679033d24e965a3dd73dfab6dfaa&chksm=ebf6d0d2dc8159c4ae291d99e9c337b0cdb05578ecf7bb43671ad02cdbecfdf916a98ab18929&scene=27#wechat_redirect

            其实,移除永久代的工作从JDK1.7就开始了。JDK1.7中,存储在永久代的部分数据就已经转移到了Java Heap或者是 Native Heap。但永久代仍存在于JDK1.7中,并没完全移除,譬如符号引用(Symbols)转移到了native heap;字面量(interned strings)转移到了java heap;类的静态变量(class statics)转移到了java heap。我们可以通过一段程序来比较 JDK 1.6 与 JDK 1.7及 JDK 1.8 的区别,以字符串常量为例:

    package com.paddx.test.memory;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class StringOomMock {
        static String  base = "string";
        public static void main(String[] args) {
            List<String> list = new ArrayList<String>();
            for (int i=0;i< Integer.MAX_VALUE;i++){
                String str = base + base;
                base = str;
                list.add(str.intern());
            }
        }
    }
    

            这段程序以2的指数级不断的生成新的字符串,这样可以比较快速的消耗内存。我们通过 JDK 1.6、JDK 1.7 和 JDK 1.8 分别运行:
    在这里插入图片描述
    在这里插入图片描述
            从上述结果可以看出,JDK 1.6下,会出现“PermGen Space”的内存溢出,而在 JDK 1.7和 JDK 1.8 中,会出现堆内存溢出,并且 JDK 1.8中 PermSize 和 MaxPermGen 已经无效。因此,可以大致验证 JDK 1.7 和 1.8 将字符串常量由永久代转移到堆中

    4)元空间是什么?

            元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制,但可以通过以下参数来指定元空间的大小:

            -XX:MetaspaceSize,初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值。

            -XX:MaxMetaspaceSize,最大空间,默认是没有限制的。

    除了上面两个指定大小的选项以外,还有两个与 GC 相关的属性:

            -XX:MinMetaspaceFreeRatio,在GC之后,最小的Metaspace剩余空间容量的百分比,减少为分配空间所导致的垃圾收集.

            -XX:MaxMetaspaceFreeRatio,在GC之后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾收集

            现在我们在 JDK 8下重新运行一下代码段 4,不过这次不再指定 PermSize 和 MaxPermSize。而是指定 MetaSpaceSize 和 MaxMetaSpaceSize的大小。输出结果如下:

    在这里插入图片描述

    5)关于为什么移除永久代?

            字符串存在永久代中,容易出现性能问题和内存溢出。
            类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出。
            永久代会为 GC 带来不必要的复杂度,并且回收效率偏低。
            Oracle 可能会将HotSpot 与 JRockit 合二为一。

    5)补充

    某种程度上说,该图也是正确的:来源:
    https://www.zhihu.com/question/300075241/answer/519570351
    在这里插入图片描述

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

    千次阅读 2018-07-04 17:55:11
     和java堆一样,方法区也是属于线程共享的区域,存放的是java的类型信息,静态变量,运行时常量池以及jit编译后的代码等数据。  运行时常量池:  class文件中除了包含类的版本,类名,字段,方法,接口等信息,...

    方法区:

            和java堆一样,方法区也是属于线程共享的区域,存放的是java的类型信息,静态变量,运行时常量池以及jit编译后的代码等数据

            运行时常量池:

        class文件中除了包含类的版本,类名,字段,方法,接口等信息,还包含了常量池,这里面存放了编译期产生的各种 字面量以及符号引用,在类加载后进入方法区的运行时常量池中,运行时常量池相对于class常量池一个重要的特征是动态性,在运行期间也可以将新的常量放入其中,用的较多的就是String的intern方法

                           符号引用:类、接口的全限定名,字段和方法的名称以及描述符;在对java文件进行编译的过程中,并不会向C语言那样有连接这一步,也就是说class文件中不会存储方法、字段的最终内存布局信息,所以符号引用是不能被虚拟机直接使用的,虚拟机会在加载类时动态的去获取常量池中的符号引用,然后解析到对应的内存地址中,才可以使用

                         字面量:比较接近于常量的概念,如文本字符串,声明为final的常量值等。

    展开全文
  • 方法区:方法区在虚拟机启动时创建,也是一块所有线程共享的内存区域,它用于存储已被虚拟机加载的类的信息,常量,静态变量,及时编译器编译后的代码数据。 运行时常量池:是方法区的一部分,...
    1. 本地方法区
    2. java堆
    3. 方法区
    4. 运行时常量池

    本地方法区:在本地方法栈中执行非java语言编写的代码,例如C或者C++。

    Java堆:是类实例和数组的分配空间,是一块线程所共享的内存区域,虚拟机启动时创建。

    方法区:方法区在虚拟机启动时创建,也是一块所有线程共享的内存区域,它用于存储已被虚拟机加载的类的信息,常量,静态变量,及时编译器编译后的代码数据。

    运行时常量池:是方法区的一部分,用于存放编译器生成的各种字面量和符号引用,者部分内容将在内加载后存放在方法区的运行时常量池中。

    直接内存:直接内存并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,但这部分被频繁的使用,而且也可能导致OOM异常出现。从JDK1.4开始,新加入了NIO(new Inpur/Output)并且引入了一种基于channel和Buffer的IO方式,它可以使用native函数数据库直接分配堆的内存,然后通过与个存储在java堆里面的DirectByteBuffer对象作为这块内存的引用操作,这样能在一些场景中显著提高性能,因为避免了在Java堆和native堆中来回复制数据

    展开全文
  • JDK1.7 及之后版本的 JVM 已经将运行时常量池方法区中移了出来, Java (Heap)中开辟了一块区域存放运行时常量池。 JDK1.8开始,取消了Java方法区,取而代之的是位于直接内存的元空间(metaSpace)。 已知:...
  • String.intern()方法的作用是返回一个字符串引用,引用的是字符串常量池中的字符串(字面量),所以我们可以通过这个方法来测试,使得字符串常量池内存溢出,看看这个时候报错报的是哪里out of memory。 import ...
  • 要是没有实践过别人书本上的理论的话,就还是会说常量池在方法区里面,要是知道方法已经随jdk升级,被逐步干掉的话,就会看到有的文章说移动到heap里面了,还有极少的说移动到Metaspace里面了,产生了分歧。...
  • 2.jdk1.6之前的常量池实现在方法区(perm),使用new关键字初始化的String,会在常量池中分别生成对象 但是在1.6中,如果intern()方法检测到只有中存在对象,而常量池没有,则会在常量池里也存入等值对象 ...
  • 代码块中定义一个变量时,java栈就为这个变量分配适当的内存空间,当该变量退出作用域时,java栈会释放该变量的内存空间。 java堆 Java堆,用来存放new创建的对象(实例)和数组。该内存的...
  • 文章目录引入:方法区常量池概述字符串常量池class常量池运行时常量池 这里介绍 字符串常量池、class常量池 和 运行时常量池 这三个常量池的概念。 引入:方法区常量池概述 方法区包含运行时常量池、自动和方法数据...
  • 字符串常量池存的是字符串常量和内的字符串对象的引用。 静态常量池(class文件常量池) 用于存放编译器生成的各种字面量(Literal)和符号引用(Symbolic References)。 字面量:文本字符串int long 等基本类型....
  • Java常量池的大概理解

    万次阅读 多人点赞 2016-07-26 10:29:39
    触摸java常量池 java常量池是一个经久不衰的话题,也是面试官的最爱,题目花样百出,小菜早就对常量池有所耳闻,这次好好总结一下。 理论 小菜先拙劣的表达一下jvm虚拟内存分布: 程序计数器是jvm执行...
  • Java常量池理解与总结

    万次阅读 2018-03-13 13:28:56
    Class文件中的常量池在Class文件结构中,最头的4个字节用于存储魔数Magic Number,用于确定一个文件是否能被JVM接受,再接着4个字节用于存储版本号,前2个字节存储次版本号,后2个存储主版本号,再接着是用于存放...
  • 在java的内存分配中,经常听到很多关于常量池的描述,我开始看的时候也是看的很模糊,网上五花八门的说法简直太多了,最后查阅各种资料,终于算是差不多理清了,很多网上说法都有问题,笔者尝试着来区分...
  • 在JDK1.7 字符串常量池被从方法拿到了中, 这里没有提到运行时常量池,也就是说字符串常量池被单独拿到,运行时常量池剩下的东西还在方法区, 也就是hotspot中的永久代 在JDK1.8 hotspot移除了永久代用元空间(Me....
  • Java常量池技术

    2013-09-25 13:53:19
    java中的常量池技术,是为了方便快捷地创建某些对象而出现的,当需要一个对象时,就可以从池中取一个出来(如果池中没有则创建一个),则需要重复创建相等变量时节省了很多时间。常量池其实也就是一个内存空间,...
  • 这几天Java虚拟机方面的知识时,看到了有几种不同常量池的说法,然后我就去CSDN、博客园等上找资料,里面说的内容真是百花齐放,各自争艳,因此,我好好整理了一下,将我自认为对的理解写下来与大家共同探讨: ...
  • Java堆、栈、方法区常量池

    千次阅读 2016-03-11 01:57:38
    1 与栈Java的数据根据不同的使用情况,有不同的分类,接下来先简单概括一下各种数据类别(不是类型)的内存分配情况,首先帮助区分一下java堆java栈: 基础数据类型(Value type)直接栈(stack)空间分配,方法的...
  • 要说Java中的栈,,方法区常量池就要提到HotSpot,HotSpot是Sun JDK 和 Open JDK中所带的虚拟机。 (Sun JDK 和 Open JDK除了注释不同,代码实现基本上是一样的)Stack(栈):分为VM Stack(虚拟机栈)和Native ...
  • Java堆、栈和常量池

    千次阅读 2013-08-28 14:50:37
    Java堆、栈和常量池 1.寄存器:最快的存储, 由编译器根据需求进行分配,我们程序中无法控制. 2. 栈:存放基本类型的变量数据和对象的引用,但对象本身不存放栈中,而是存放在堆(new 出来的对象)或者常量池...
  • java常量池技术

    千次阅读 2013-11-18 10:26:26
    java中的常量池技术,是为了方便快捷地创建某些对象而出现的,当需要一个对象时,就可以从池中取一个出来(如果池中没有则创建一个),则需要重复创建相等变量时节省了很多时间。常量池其实也就是一个内存空间,...
  • 1,java内存模型简介 《深入理解java虚拟机》里将java内存分为如下五个模块: 是所有线程共享的,主要用来存储对象...Java虚拟机栈/本地方法栈-线程私有的,主要存放局部变量表,操作数栈,动态链接和方...
  • java堆、栈、方法区常量池

    千次阅读 2013-08-29 09:54:23
     当一段代码块定义一个变量时,Java栈中为这个变量分配内存空间,当该变量退出该作用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。  每个线程包含一个栈,每个栈中...
  • java常量池概念

    万次阅读 多人点赞 2012-10-23 21:07:14
     Java是一种动态连接的语言,常量池的作用非常重要,常量池中除了包含代码中所定义的各种基本类型(如int、long等等)和对象型(如String及数组)的常量值还,还包含一些以文本形式出现的符号引用,比如: ...
  • 最近看《深入理解Java虚拟机》,书中给了几个例子,比较好的说明了几种...Java程序运行时,数据会分区存放,JavaStack(Java栈)、 heap()、method(方法区)。1、JavaJava栈的区域很小,只有1M,特点是存...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 91,563
精华内容 36,625
关键字:

java常量池在方法区还是堆

java 订阅