java 内存分析

java 内存分析


2019年9月20日18:55:50 更新
今天主要去看了一下jvm解析,感觉这篇博客写的很片面,很多东西都说对不对说错不错.
也不进行删除了,留待给自己一个警醒. 后续我会先把jvm解析的课程看完,一部分一部分的将jvm的部分写出来.


最近感觉java基础知识不是很牢固,所以又返回去着重看了一些java的基础知识.
目标主要是两个:

  1. 搞懂java中的内存是怎么分配的
  2. 搞清楚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的内存分配情况下面还会细讲.

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赋值


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.
new创建字符串的比较


@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().


   转载规则


《java 内存分析》 echi1995 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
jvm的运行机制(一) java虚拟机示意图 jvm的运行机制(一) java虚拟机示意图
今天去看了一下jvm的机制, 在B站上看一个叫程序员诸葛讲的8月最新java虚拟机JVM运行机制讲解视频全集目前学到第6p, 会一点一点的将老师讲的课, 和在听课过程中记得笔记分享上来。 这篇博客的内容主要是jvm的结构,将画出示意图。 假
下一篇 
在spring中获取applicationContext. 在spring中获取applicationContext.
以前想要获取applicationContext, 在网上搜索后也很麻烦,不好用.今天在看spring文档时, 发现文档上写了一种很简单的获取方法(Spring Framework Version: 4.3.21.RELEASE): 您
  目录