目录
- 概述
- 字符串常量池
- 字符串常量池存储数据的方式
- 三种常量池
- 字面量与符号引用
- 哈希表
- 实战
- 程序计数器
- 直接内存
- 直接内存与堆内存比较
- 结束
概述
相关文章在此总结如下:
文章 | 地址 |
---|---|
jvm基本知识 | 地址 |
jvm类加载系统 | 地址 |
双亲委派模型与打破双亲委派 | 地址 |
运行时数据区 | 地址 |
字符串常量池
字符串常量池存储数据的方式
字符串常量池使用的是StringTable的数据结构存储数据,类似于HashTable(哈希表)
注意: 双引号表示的字符串,默认都是存在字符串常量池的。堆内存中的字符串想进入字符串常量池,也不是不可的。
三种常量池
- Class文件常量池
- 运行时常量池
- 字符串常量池
字面量与符号引用
字面量: int 、float、long、double、双引号字符串等
符号引用:Class、Method、Field等
哈希表
哈希表(也叫散列表),是根据关键码值(K-V)而直接进行访问的数据结构。本质上就是个数组+链表
- key:散列函数,公式:hash(字符串) % 数组size
- value:字符串的引用
- size: -XX:StringTableSize=65536
目标:加速查找速度
实战
测试案例如下:
package com.fun.classloader;
/**
* 测试字符串常量池
*/
public class TestStringPool {
public static void main(String[] args) {
test();
}
public static void test() {
String str1 = "abc";
String str2 = new String("abc");
// false == 比较的是对象内存地址
System.out.println(str1 == str2);
String str3 = new String("abc");
// false
System.out.println(str2 == str3);
// 编译期会计算出 "ab"
String str4 = "a" + "b";
// true
System.out.println(str4 == "ab");
String s1 = "a";
String s2 = "b";
String str6 = s1 + s2;
// false 变量相加运行期计算
System.out.println(str6 == "ab");
// 带方法的运算也是运行期计算
String str7 = "abc".substring(0, 2);
// false
System.out.println(str7 == "ab");
String s5 = "a";
String s6 = "abc";
String s7 = s5 + "bc";
// true
System.out.println(s6 == s7.intern());
}
}
总结如下:
- 单独使用""引号创建的字符串都是常量,编译期就已经确定存储到字符串常量池中
- 使用 new String(“”) 创建的对象会存储到
heap
中,是运行期新创建的 - 使用只包含常量的字符串连接符,如:“a”+“b” 创建的也是常量,编译期就已经确定存储到字符串常量池中
- 使用包含变量的字符串连接如 “a” + s 创建的对象是运行期才创建的,存储到 heap 中
- 运行期调用 String 的 intern() 方法可以向字符串常量池中动态添加对象
程序计数器
程序计数器,也叫PC寄存器、当前线程所执行的字节码指令
的行号指示器
**注意:**线程切换(系统上下文切换) 后准确恢复执行位置,需要知道执行到的位置
存储数据
- java 方法:记录虚拟机字节码指令地址
- native方法:记录为空
异常:唯一没有OOM异常的区域
直接内存
直接内存不是虚拟机运行时数据区的一部分,也不是 java 虚拟机规范
中定义的内存区域
在 jdk1.4 中,新加了 NIO,引入了 Channel 和 Buffer 的 IO 方式,可以使用 native 方法直接分片对外内存,然后通过 DirectByteBuffer
对象可以操作直接内存。
直接内存与堆内存比较
内存区域 | 分配空间 | 读写操作 |
---|---|---|
堆内存 | 性能很好 | 效率低 |
直接内存 | 性能很差 | 效率高 |
结束
至此,字符串常量池、程序计数器、直接内存,就结束了,如有疑问,欢迎评论区留言。