java 内存分析
2019年9月20日18:55:50 更新
今天主要去看了一下jvm解析,感觉这篇博客写的很片面,很多东西都说对不对说错不错.
也不进行删除了,留待给自己一个警醒. 后续我会先把jvm解析的课程看完,一部分一部分的将jvm的部分写出来.
最近感觉java基础知识不是很牢固,所以又返回去着重看了一些java的基础知识.
目标主要是两个:
- 搞懂java中的内存是怎么分配的
- 搞清楚API的实现,如 String/数组/Map等API的实现.
这篇是关于java中的内存分析
1. 运行时内存划分大致区块:
根据《Java虚拟机规范》的规定,运行时数据区通常包括这几个部分:程序计数器(Program Counter Register)、Java栈(VM Stack)、本地方法栈(Native Method Stack)、方法区(Method Area)、堆(Heap)。
实际关注主要集中在栈区和堆区
2. 各种类型内存分析
2.1. 基本数据类型
基本数据类型直接存放在栈区,栈中存放的值就等于基本数据类型的值,如
char a1 = 'a';
byte a2 = 1;
short a3 = 1;
int a4 = 1;
long a5 = 1L;
float a6 = 0.1f;
double a7 = 0.1;
boolean a8 = true;
2.2. 引用类型
引用类型分两部分,一部分是引用,一部分是对象.
引用部分存放在栈区,值为对象的内存地址.(实际不是,只是根据这个值可以找到对象)
对象实际存在堆区.
如, Person person = new Person();
在栈区存在一小块内存,存放的是该对象在堆区中的地址.
2.3. String
String比较特殊,String有两种方式实例化
String s1 = "hello world";
String s2 = new String("hello world");
这两种实例化方式对应不同的内存分配情况.
① 通过字符串常量的方法实例化一个字符串对象: 字符串引用会直接指向字符串常量池.
② 通过new的方式实例化一个字符串对象: 字符串引用会先指向堆中的string对象,由对象指向字符串常量池.
因此s1直接指向方法区的字符串常量池,而s2指向堆区.两种方法指向的都是同一个字符串.
关于String的内存分配情况下面还会细讲.
2.4. 数组
数组类型也是一个引用,指向内存中连续的一片空间.
int[] arr1;
int[] arr2 = new int[5];
3. 一些具体情况
@Test
public void test01(){
String string = "hello";
System.out.println(string);// hello
change(string);
System.out.println(string);// hello
}
public String change(String string){
string = "world";
return string;
}
初始化一个String,赋值为”hello”,将string传入一个方法里,在这个方法里对string重新赋值,最后输出结果没有改变.
原因是,在对象传值到另一个方法中时,传入的并非引用本身,而是引用存的地址值.
test01.string传入到change()中后,栈区中出现了一个新的引用姑且叫他change.string, change.string也指向”hello
“.在change方法中对change.string重新赋值,使change.string指向”world
“,test01.string并没有改变指向.
String类中重写了equals()方法,只要两个字符串值相等equals()就返回true. 而==
操作符比较的是两个对象的内存地址.
@Test
public void test02(){
String s1 = "hello";
String s2 = "hello";
System.out.println(s1 == s2); // true
System.out.println(s1.equals(s2)); // true
}
因为两个字符串对象都是通过字符串常量的方式实例化出来的,他们均指向字符串常量池.因此下面两个输出都是true
@Test
public void test03(){
String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1 == s2); // false
System.out.println(s1.equals(s2)); // true
}
因为两个字符串都是通过new的方式实例化出来的,因此两个字符串分别指向堆中的两个对象,两个对象均指向字符串常量池.
因此比较两个字符串时,比较的是两个堆中的对象的地址值. 因为是两个不同的对象,所以使用==
比较结果是false.
@Test
public void test04(){
String s1 = "hello";
String s2 = "world";
String s3 = "helloworld";
String s4 = "hello" + "world";
String s5 = s1 + "world";
System.out.println(s3 == s4); // true
System.out.println(s3 == s5); // false
}
为什么这次输出不一样? 明明”hello” + “world”拼接起来和原字符串相等, 为什么s1拼接后的字符串就不相等了呢?s1不也是指向字符串常量池吗?
字符串的拼接如果只有常量参与,那么拼接后的结果还是指向常量池.
一旦有变量参与字符串的拼接,那拼接后的结果就会指向堆区,由堆区指向常量池.
在最后抛出一个疑问. 数组是引用类型,字符串也是引用类型, 为什么int[].length而string.length().