`

jvm之String 在jvm中分配

阅读更多
----------~开篇分享一句话:【纸上得来终觉浅,绝知此事要躬行】~---------------------------------------

关于String的些许探讨
首先看下面的案例:
/**
	 * JVM对于字符串常量的"+"号连接,将程序编译期,JVM就将常量字符串的"+"连接优化为连接后的值,拿"a" + 1来说,经编译器优化后在class中就已经是a1。在编译期其字符串常量的值就确定下来,故上面程序最终的结果都为true。
	 */
	@Test
	public void testConstant() {
		if (true) {
			String a = "a1";
			String b = "a" + 1;
			System.out.println((a == b)); // result = true
		}
		if (true) {
			String a = "atrue";
			String b = "a" + "true";
			System.out.println((a == b)); // result = true
		}
		if (true) {
			String a = "a3.4";
			String b = "a" + 3.4;
			System.out.println((a == b)); // result = true
		}
	}

	/**
	 * JVM对于字符串引用,由于在字符串的"+"连接中,有字符串引用存在,而引用的值在程序编译期是无法确定的,即"a" + bb无法被编译器优化,只有在程序运行期来动态分配并将连接后的新地址赋给b。所以上面程序的结果也就为false。
	 */
	@Test
	public void testConstantAB() {
		String a = "ab";
		String bb = "b";
		String b = "a" + bb;
		System.out.println((a == b)); // result = false
	}

	/**
	 * 分析:和上面testConstantAB中唯一不同的是bb字符串加了final修饰,对于final修饰的变量,它在编译时被解析为常量值的一个本地拷贝存储到自己的常量池中或嵌入到它的字节码流中。所以此时的"a" + bb和"a" + "b"效果是一样的。故上面程序的结果为true。
	 */
	@Test
	public void testConstantFinalAB() {
		String a = "ab";
		final String bb = "b";
		String b = "a" + bb;
		System.out.println((a == b)); // result = true
	}

	/**
	 * JVM对于字符串引用bb,它的值在编译期无法确定,只有在程序运行期调用方法后,将方法的返回值和"a"来动态连接并分配地址为b,
	 * 故上面程序的结果为false。
	 */
	@Test
	public void testConstantFinalFunctionAB() {
		String a = "ab";
		final String bb = getBB();
		String b = "a" + bb;
		System.out.println((a == b)); // result = false
	}

	private static String getBB() {
		return "b";
	}



------------------------------------------------------------------------------------------------------------------
分析结论:
------------------------------------------------------------------------------------------------------------------

1、String a = "a1"; 变量a对应的字符串a1 分配在了PSPermGen的常量池。
证明思路:
jvm参数配置:-Xmx200M -Xms200M -Xmn100M -XX:SurvivorRatio=8   -XX:+PrintGCDetails -verbose:gc
java代码:
public static void main(String[] args) {
  System.gc();
}

日志:
PSPermGen       total 21248K, used 3054K [0x00000000ee600000, 0x00000000efac0000, 0x00000000f3800000)
public static void main(String[] args) {
		System.gc();
		String s1= "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
		String s4= "baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
		System.gc();

日志:
PSPermGen       total 21248K, used 3057K [0x00000000ee600000, 0x00000000efac0000, 0x00000000f3800000)
证明 PSPermGen区由3054K增长了3K到3057K

2、String a = "ab"; final String bb = "b"; String b = "a" + bb; b同样分配在了常量区
证明思路:
public static void main(String[] args) {
		System.gc();
		final String s1= "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
		final String s4= "baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
		String s5 = s1 + s4;
		System.gc();

PSPermGen       total 21248K, used 3060K [0x00000000ee600000, 0x00000000efac0000, 0x00000000f3800000)
证明:PSPermGen区由3057K->3060K,增长的依旧常量池。

3、String a = "ab"; String bb = "b"; String b = "a" + bb; b分配在了堆区。
证明思路:
方法1、调试 b = "a" + bb; 你会发现代码进入了StringBuilder
方法2如下:
public static void main(String[] args) {
  System.gc();
}

日志:
PSYoungGen      total 92160K, used 3276K [0x00000000f9c00000, 0x0000000100000000, 0x0000000100000000)
eden space 81920K, 4% used [0x00000000f9c00000,0x00000000f9f333b0,0x00000000fec00000)
public static void main(String[] args) {
		String s1= "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
		String s4= "baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
		long begin = Runtime.getRuntime().freeMemory();
		 for(int i=0;i<300;i++){
			String temp =  s1+"-"+i+"-"+s4;
		 }		 
		long end = Runtime.getRuntime().freeMemory();
		System.out.println((begin - end)/1024 + "KB");
	}	

日志:
PSYoungGen      total 92160K, used 4916K [0x00000000f9c00000, 0x0000000100000000, 0x0000000100000000)
eden space 81920K, 6% used [0x00000000f9c00000,0x00000000fa0cd160,0x00000000fec00000)
对比说明eden 内存量在减少,说明"a" + bb 在jvm的存在是堆的eden区
0
0
分享到:
评论

相关推荐

    JVM内存分配与垃圾回收详解

    JVM 内存分配与垃圾回收是 JVM 中两个非常重要的概念,本文将对 JVM 内存分配与垃圾回收进行详细的解释。 JVM 运行时数据区域 JVM 内存分配主要涉及到五个运行时数据区域:程序计数器、Java 虚拟机栈、本地方法栈...

    java 查看JVM中所有的线程的活动状况

    本文将详细讲解如何查看JVM中的线程活动情况,并提供相关示例代码。 首先,Java提供了`java.lang.management.ThreadMXBean`接口,它是管理JVM线程的管理接口。通过这个接口,我们可以获取线程的各种信息,包括线程...

    JVM必知必会

    - **JVM参数**:可以配置JVM启动时的参数,用于优化内存分配、垃圾回收等方面。 - **类文件解析**:JVM将类文件中的字节码转换成计算机能够理解的指令执行。 #### 10. 常用工具 - **VisualVM**:是一个可以监控运行...

    JVM指令手册详细完整版.pdf

    例如ldc命令将int, float或String型常量值从常量池中推送至栈顶,ldc_w命令将int, float或String型常量值从常量池中推送至栈顶(宽索引),ldc2_w命令将long或double型常量值从常量池中推送至栈顶(宽索引)。...

    JVM系列之String.intern的性能解析

    在Java虚拟机(JVM)中,`String.intern()` 是一个非常特殊且重要的方法,它与字符串常量池紧密相关。字符串常量池是一种内存优化机制,存储了程序中所有的字符串字面值和通过 `intern()` 方法添加的字符串。这个池...

    Java JVM Instruction Set

    `main()`函数通常包含初始化参数`String[] args`,这部分的局部变量会在栈帧中分配空间。 ##### Min()函数的调用 当从`main()`函数中调用`Min()`函数时,JVM会创建一个新的栈帧来保存局部变量和参数,并跳转到相应...

    JVM 深入学习教程深入分析JVM教程!jvm 内存原型,优化等等

    Java中的动态内存分配主要在堆上进行,垃圾收集器也会对堆进行管理,进行垃圾回收。 2. 方法区:存储类信息、常量、静态变量等,也被称为非堆或永久代。在较新的JVM版本中,这部分已经被元空间(Metaspace)所替代...

    JVM基础.doc

    **ClassLoader** 负责加载ClassFile到JVM中,它遵循双亲委派模型。常见的ClassLoader包括Bootstrap ClassLoader、Extension ClassLoader和App ClassLoader。 #### 四、内存模型、锁与同步 **Java内存模型** 主要...

    深入理解JVM实战篇-String类1

    在Java编程中,String类是使用最频繁的类之一,它涉及到许多底层机制,特别是与JVM(Java虚拟机)的交互。本文将探讨String类的一些关键特性,包括字面量与运行时常量池、String的intern()方法以及它们如何影响内存...

    创建string对象过程的内存分配:

    `String` 对象的创建涉及复杂的内存分配机制,特别是在Java虚拟机 (JVM) 的环境中。本文将详细介绍创建 `String` 对象过程中涉及的不同内存区域,以及不同创建方式下内存分配的过程。 #### 二、内存分配概述 在Java...

    java虚拟机JVM详解ppt

    在某些JVM实现中,它与Java虚拟机栈合并在一起。 ##### 4.5 方法区(Method Area) 方法区存储了每个类的信息(包括类的方法和字段)、常量池、静态变量等。它是共享的内存区域,用于存放所有类的信息。 #### 五、...

    JVM性能优化(PPT)

    在Java开发中,JVM(Java Virtual Machine)是至关重要的组成部分,它负责运行所有的Java应用程序。JVM性能优化是一项细致而关键的任务,能够显著提升程序的运行效率,减少资源消耗,提高系统稳定性。本PPT详细探讨...

    jvm 加载class文件

    Java程序的运行依赖于类加载器将`.class`文件加载到JVM中,并对其进行验证、准备和解析等步骤,最终使类能够在JVM中执行。 #### 三、Class文件的加载过程 Java类的加载过程主要可以分为以下几个阶段: 1. **加载...

    Java 6 JVM参数选项大全

    新生代收集担保是确保在Minor GC期间,如果新生代的对象无法全部转移到Survivor区,JVM有足够的空间在老年代进行分配。关闭此功能可能导致在某些情况下更频繁的Full GC,但可以减少内存浪费和潜在的性能提升。 在...

    JVM内存分配及String常用方法解析

    常量池是JVM中一个重要的概念,用于存储编译期间生成的字面量和符号引用。常量池有两种形式:静态常量池和运行时常量池。 静态常量池是class文件中的常量池,包含字符串(数字)字面量、类、方法的信息,占用class...

    Sun JVM原理与内存管理

    - 特殊情况下直接在 Old 区分配,如大对象或长时间存活的对象。 - TLAB (Thread Local Allocation Buffer) 是一种优化技术,允许每个线程拥有自己的缓冲区,从而加快分配速度。 2. **栈上分配**: - 适用于原子...

    jvm内存参数调优

    如果`value`中有空格,则需要用双引号将该值括起来,如`-Dname="space string"`。这个参数常用于设置系统级全局变量值,例如配置文件路径。 - **示例**:`-Dconfig.file=C:\conf\application.properties`。 #####...

    jvm8虚拟机规范

    原始类型在内存中直接存储值,而引用类型存储的是对象的引用地址。 3. 类文件格式: Java源代码编译后生成.class文件,其内部结构遵循特定的格式。主要包括魔数、版本号、常量池、访问标志、类索引、父类索引、...

    jvm瓶颈定位 java jvm 学习

    - 直接内存:不在JVM堆中的内存区域,使用Native Memory API直接分配。 3. **JVM性能监控与调优工具** - JConsole:Java自带的图形化监控工具,可以查看JVM的内存、线程、类加载等信息。 - VisualVM:强大的多...

Global site tag (gtag.js) - Google Analytics