字符串常量在内存中的存放位置由系统自动安排_java内存空间分析

上次面试中被问到了static和final修饰的变量的内部存储空间,虽然我答出来了,但是我也是有点不确定,于是准备总结一波!

运行时的数据区

668eaa33dc72d90de4d1aab065375a0e.png

1.程序计数器:

我们在程序中无法控制。最快的保存区域,位于处理器内部,由编译器分配。主要作用是记录当前线程所执行的字节码的行号。字节码解释器工作时就是通过改变当前线程的程序计数器选取下一条字节码指令来工作。任何分支、循环、方法调用、判断、异常处理、线程等待以及恢复线程、递归等都是通过这个计数器来完成。为了多线程的实现,每条线程都会有独立的程序计数器来记录当前指令的行号,以使得线程等待结束后能恢复到正确的位置执行。这块内存也被称为"线程私有"的内存。如果调用的方法是native的,则寄存器不存储任何信息。

2、JVM栈

在函数中定义的一些基本类型的变量数据(8种)和对象的引用变量都在函数的栈内存中分配。非基本类型的对象(引用类型)在JVM栈上只存放一个指向堆内存的地址,因此Java中的基本类型变量采用值传递,而引用类型对象采用引用传递。

JVM的栈是线程私有的,每个线程创建的同时都会创建JVM栈。因此内存分配上非常高效,且当线程运行完毕后,这些内存也就被自动回收另作它用。当栈请求深度大于允许的深度时,会抛出StackOverflowError的错误,可以通过-Xss来指定栈的大小。而如果内存不足时,会抛出OutOfMemoryError错误。

3、堆

堆内存用来存放由new创建的对象和数组。堆中的对象内存需要等待GC(垃圾回收机制)进行回收,堆内存在32位操作系统上最大为2G,在64位操作系统上则没有限制,其大小通过-Xms(初始堆大小,即启动最小堆内存,默认物理内存的1/64,小于1G)和-Xmx(启动最大堆内存,默认物理内存的1/4)来控制。默认当空余堆内存小于40%时,JVM会增大堆的大小到-Xmx指定的大小,可通过-XX:MinHeapFreeRatio来指定这个比例。当空余堆内存大于70%时,JVM会将堆内存的大小往-Xms指定的大小调整,可通过-XX:MaxHeapFreeRatio=来指定这个比例,但对于运行系统而言,为了避免频繁的改变堆内存的大小,通常都会将-Xms和-Xmx的值设置成一样。当堆中需要使用的内存超过其允许的大小时,会抛出OutOfMemory的错误信息。

在Java中,程序员基本不用去关心空间释放的问题,Java的垃圾回收机制会自动进行处理。因此这部分空间也是Java垃圾收集器管理的主要区域。另外,堆是被所有线程共享的,在JVM中只有一个堆。

和栈比起来,编译器不需要知道要从堆里分配多少存储区 域,也不必知道存储的数据在堆里存活多长时间。因此更加灵活,但是速度更慢些。

4、本地方法栈

本地方法栈与Java栈的作用和原理非常相似。区别只不过是Java栈是为执行Java方法服务的,而本地方法栈则是为执行本地方法(Native Method)服务的。在JVM规范中,并没有对本地方法栈的具体实现方法以及数据结构作强制规定,虚拟机可以自由实现它。在HotSopt虚拟机中直接就把本地方法栈和Java栈合二为一。

5、方法区

方法区在JVM中也是一个非常重要的区域,它与堆一样,是被线程共享的区域。用永久代来实现了方法区在方法区中,存储了每个类的信息(包括类的名称、方法信息、字段信息)、静态变量、常量以及编译器编译后的代码等。static修饰的变量存储于静态域就属于方法区。

在Class文件中除了类的字段、方法、接口等描述信息外,还有一项信息是常量池,用来存储编译期间生成的字面量和符号引用,这部分内容会在类加载后进入方法区的运行时常量池中存放。

需要注意的:

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

如以下代码: Java代码

        String s1 = "china"; 
        String s2 = "china";
        String s3 = "china"; 
        String ss1 = new String("china"); 
        String ss2 = new String("china"); 
        String ss3 = new String("china");   

这里解释一下,对于通过 new 产生一个字符串(假设为 ”china” )时,会先去常量池中查找是否已经有了 ”china” 对象,如果没有则在常量池中创建一个此字符串对象,然后堆中再创建一个常量池中此 ”china” 对象的拷贝对象。

也就是有道面试题: String s = new String(“lh”); 产生几个对象?

一个或两个。如果常量池中原来没有 ”lh”, 就是两个。如果原来的常量池中存在“lh”时,就是一个。

对于基础类型的变量和常量:变量和引用存储在栈中,常量存储在常量池中。

简单总结起来:

  • ◆寄存器:我们在程序中无法控制
  • ◆栈:存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而是存放在堆中
  • ◆堆:存放用new产生的数据
  • ◆静态域:存放在对象中用static定义的静态成员
  • ◆常量池:存放常量
  • ◆非RAM存储:硬盘等永久存储空间

jdk8的变化:

移除了PermGen,取而代之的是MetaSpace元空间:
一种新的内存空间的诞生。JDK8 HotSpot JVM 使用一个与堆不相连的本地内存来存储类元数据信息并称之为:元空间(Metaspace);这与Oracle JRockit 和IBM JVM’s很相似。这将是一个好消息:意味着不会再有java.lang.OutOfMemoryError:PermGen问题,也不再需要你进行调优及监控内存空间的使用,但是新特性怒能消除类和类加载器导致的内存泄漏。你需要使用不同的方法以及遵守新的命名约定来追踪这些问题。
详细可见:http://openjdk.java.net/jeps/122

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 字符串常量池在JVM属于方法区(也称为永久代)内存分区。方法区是各个线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。字符串常量池是方法区的一部分,用于存储字符串常量。在JDK 8之后,方法区被取消了,取而代之的是元空间(MetaSpace),但字符串常量池仍然存放在元空间。 ### 回答2: 字符串常量池在JVM的方法区(也称为非堆区)。 JVM将内存分为几个不同的区域,包括堆区、方法区、虚拟机等。而字符串常量池是方法区的一部分,用于存储在程序直接使用的字符串常量。 在Java字符串常量池是一种特殊的内存存储区域,用于存储字符串常量,它的作用是提高字符串的重用性和效率。当我们使用双引号声明一个字符串时,JVM会首先在字符串常量查找是否存在相同内容的字符串,如果存在则直接返回引用,如果不存在则创建一个新的字符串并放入字符串常量。这种机制可以减少内存占用,提高程序的执行效率。 由于字符串常量池位于方法区,它是与其他线程共享的,在程序运行过程,多个线程可以同时访问字符串常量池。而且,字符串常量池的位置是在程序的执行过程被动态调整的,当字符串没有被引用时,JVM会自动回收字符串常量的空间。 总结来说,字符串常量池是JVM的方法区的一部分,用于存储程序直接使用的字符串常量,并提高字符串的重用性和效率。 ### 回答3: 字符串常量池在JVM的方法区里。方法区是JVM的一个内存分区,用于存储类信息、常量、静态变量、即时编译器编译后的代码等。而字符串常量池就是方法区的一部分,用于存储字符串常量。 在Java,当我们使用字符串字面量(如"hello")时,编译器会将其放入字符串常量。当程序执行时,如果再次使用相同的字符串字面量,JVM会直接从字符串常量取出已存在的字符串对象,而不会创建新的对象,这样可以节省内存空间。 由于字符串Java使用非常频繁,所以将字符串常量池放在方法区,可以提高字符串的重用率。此外,字符串常量池的位置在方法区也有利于GC(垃圾回收),因为当某个字符串不再被引用时,GC可以更方便地回收该字符串常量。 需要注意的是,从Java 7开始,字符串常量池被移出了PermGen空间(方法区的前身),并放置在堆,这是因为字符串常量字符串对象是可以被垃圾回收的,而且过多的字符串常量可能导致PermGen空间溢出的问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值