Java内存区域

  • 全局变量————》堆,

  • 局部变量————》栈,

  • 静态变量————》方法区,

  • 字符串和基本类型的常量————》方法区的静态常量池

  •  
  •  
  • 程序计数器: 
    • 线程私有的,他是一块较小的内存空间,他相当字节码于解释器中的指针,也就是该内存存放下一条即将执行指令的地址。字节码解释器就是通过改变 这个计数器的值来选择下一条即将执行的指令。每一个线程都有一个程序计数器(内存),这样线程切换的时候就能找到自己各个线程各自即将执行的下一条指令。 所以说是线程私有的。
  • java虚拟机栈: 
    • 线程私有的,每一个方法在执行的时候就会创建一个栈帧来存放方法的局部变量操作数栈返回地址等,当方法执行完成的时候就释放该栈帧。 
      栈帧:虚拟机栈中是一栈帧为单位存储的,所以一个虚拟机栈中有很多栈帧,每一个栈帧中分为:局部变量区(存放方法的参数和局部变量),操作数栈,方法的返回地址,动态链接(一般解析解阶段是将部分符号引用转换成直接应用(类加载),而动态链接是另外一部分的符号引用转换成直接引用(运行时))
  • 本地方法栈: 
    • 线程私有,本地方法指的是那种不是用java语言写的方法,java虚拟机栈只针java方法,而不是本地方法。hotspot虚拟机支持别的语言写的方法在虚拟机上运行,本法方法栈和java虚拟机栈一样。只是他们服务的对象不一样而已,一个为java方法服务,一个为native方法服务。
  • Java堆: 
    • 线程共享的,唯一的目的就是存放对象实例数组类的非静态成员变量值(基本数据类型),不过也可能为多个线程分配私有的buffer,也就是每个线程有自己的缓存器,java堆可以是物理上连续的,也可以是不连续的。java堆是垃圾回收器管理的主要区域,所以也叫gc堆。java堆可以分为:新生代和老年代
  • 方法区: 存储着class文件的信息动态常量池(方法区里存放着类的版本,字段,方法,接口和常量池。常量池里存储着字面量(字面量相当于java的常量,如字符串和声明为final的常量值)和符号引用

    •  
  • 线程共享的,可以理解为gcc中所所说的静态区,不过也不是确切的准确,因为在hotspot虚拟机中他存放的是类中静态变量和常量(注意是常量哦)编译器编译后的代码等。因为他能存储常量,所以还有存储常量的区域有一个特别的名称,叫做常量池(包括引用和基本数据类型的常量),方法区并不是堆,这一点和静态区很相似。所以别名叫non-heap,java堆中可以选择不实现gc回收,但是实际上呢还是会的,只能说垃圾回收器在这个区域不活跃而已,但是回收都是回收常量池中的常量,而不是静态变量。可以称为永久代。
  • 运行时常量池: 
    • 他是方法区的一部分,但是和方法区的常量池有区别,他存放的常量是在运行时产生的,而不是编译时产生的。注意与普通方法区的区别

  常量池中存储了相应类型所用到的所有类型字段方法符号引用

静态常量池和动态常量池的关系以及区别

静态常量池存储的是当class文件被java虚拟机加载进来后存放在方法区的一些字面量和符号引用,字面量包括字符串,基本类型的常量符号引用其实引用的就是常量池里面的字符串,但符号引用不是直接存储字符串,而是存储字符串在常量池里的索引。

动态常量池是当class文件被加载完成后,java虚拟机会将静态常量池里的内容转移到动态常量池里,在静态常量池的符号引用有一部分是会被转变为直接引用的,比如说类的静态方法或私有方法,实例构造方法,父类方法,这是因为这些方法不能被重写其他版本,所以能在加载的时候就可以将符号引用转变为直接引用,而其他的一些方法是在这个方法被第一次调用的时候才会将符号引用转变为直接引用的。

 

总的来说:方法区存储类型信息(包括版本,字段,方法,接口)和常量池,静态常量池存储字面量(字符串和基本类型的常量)和符号引用,动态常量池存储直接引用

对象访问

Object obj=new Object();假设这句出现待方法体中,

  • 那么"Object obj" 则部分会映射到Java栈的本地变量表中,作为一个reference类型数据出现,
  • "new Object()"则映射到Java堆中,形成一块存储了Object类型的所有数据值的结构化内存,还包括能查到此对象类型数据(如对象类型,父类,实现接口,方法等)的地址信息,
  • 而这些类型数据存储在方法区中

那么reference是如何访问Java堆中对象的位置呢:

①使用句:Java堆会划分一块区域存储句柄池,reference中存储的是对象的句柄地址,句柄中包括对象实例数据和数据类型的各自的具体地址信息:

②使用直接指针:reference中直接存储的就是对象的地址信息

两种方式有各自的优点,使用句柄,在对象被移动的时候自需要给吧句柄中的实例数据指针,而reference本身不需要被修改

使用直接指针最大的好处是速度更快,节省了一次指针定位时间,由于对象在java中频繁范围,所以也是节省也是可观的

声明一个类:

public class A {
    public final String tempString="world";//这里可以把final去掉,结果等同!!
    public final char[] charArray="Hello".toCharArray();
    public char[] getCharArray() {
        return charArray;
    }
    public String getTempString() {
        return tempString;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

创建测试类:

public class TestA {
    public static void main(String[] args) {
        A a1=new  A();
        A a2=new A();

        System.out.println(a1.charArray==a2.charArray);
        System.out.println(a1.tempString==a2.tempString);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

输出结果:

false
true
  • 1
  • 2

要想明白上面字符串对比为什么输出为true你必须知道:

这里写图片描述

该图片截自《深入理解Java虚拟机》

  • 一个Class字节码文件的Class字节码文件对象只有一个常量池,常量池被所有线程共享。
  • 在常量池中,字符串被存储为一个字符序列,每个字符序列都对应一个String对象,该对象保存在堆中。所以也就是说为什么String temp="xxx";能够当成一个对象使用!!
  • 如果多个线程去访问A类中的String字符串,每次都会到常量区中去找该字符序列的引用。
  • 所以访问A类被创建的两个A类型对象的String字符串对比会输出true。

如果对字符串输出为true还是不懂,可以参考这篇博客:字符串被存储的原理过程

那么为什么final类型的字符数组就不为true了呢??

申明(不管是通过new还是通过直接写一个数组)一个数组其实在Java中就等同创建了一个对象,即每次创建类的对象都会自动创建一个新的数组空间。

其中要注意的是:常量池中存储字符数组只是存储的是每个字符或者字符串。

为了证明每次获取的final数组地址不一样,并且数组中的字符都会存储在常量池中,我们需要参考另外一个代码:

public class A {
    public String tempString="world";
    public final String tempStringArray[]={"Fire","Lang"};
    public final char[] charArray={'h','e','l','l','o'};
    public Character charx='l';

    public char[] getCharArray() {
        return charArray;
    }
    public String getTempString() {
        return tempString;
    }
    public String[] getTempStringArray() {
        return tempStringArray;
    }
    public Character getCharx() {
        return charx;
    }
}

测试代码:

public class TestA {
    public static void main(String[] args) {
        A a1=new  A();
        A a2=new A();
        System.out.println(a1.tempString==a2.tempString);
        System.out.println(a1.tempStringArray==a2.tempStringArray);//看这里
        System.out.println("#####################");//看这里
        System.out.println(a1.tempStringArray[0]==a2.tempStringArray[0]);
        System.out.println(a1.tempStringArray[0]=="Fire");
        System.out.println("#####################");
        System.out.println(a1.charArray==a2.charArray);
        System.out.println(a1.charx==a2.charx);
    }
}

输出:

 

可以看到每次输出的final数组地址都不一样,最重要的是String类型的数组地址也都不一样!!但是String类型数组中的每个字符串都存储在常量池中。

所以可以肯定的是字符串和其它能够确定值的final字面量值是存储在常量池的!!并且在方法区内存中只有一份!!与所有线程共享访问!!

常量池存储的项目类型:

这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值