在编写程序时,我们也许会有这样一个问题,我们编写的程序中的数据运行时,会保存在哪里呢?简单直接的回答可能是——内存。这个回答在多数情况下可能都是对的,但事实上并不准确,我们都知道内存,即随机访问存储器可以在程序运行时保存程序所需要的数据,但不是所有数据,而且,内存这个词也并不准确。接下来,让我们看一下在程序运行时,数据可能会被存放的几大位置。

数据的六大存放位置

java为我们提供了不同于以往语言的新特性,其中一个非常方便的特性就是我们在创建新的类型(对象)时不需要手动分配内存,更不需要手动去销毁不再需要的对象,java让我们远离了对内存的繁琐操作。但是,在我们编写java程序时,依然需要做到对程序运行时数据的存放位置做到心中有数,这其中包括了寄存器、内存以及持久化存储。实际上,程序会使用到的数据,可能分配在以下六个位置:

(1) 寄存器。这是最快的保存区域,因为它是位于处理器内部,它的地理优势决定了存取都非常迅速。但是,寄存器的大小十分有限,所以它是根据编译器来分配的,我们没有直接分配的权利。也就是说,在java中,我们无法直接决定将哪些数据存放在寄存器中,也无法从程序中分析哪些数据可能会被存放在寄存器中。

     图1 CPU

(2) 堆栈。这是存取速度次快的位置,它驻留于常规RAM(随机访问存储器)区域,也就是我们常说的内存。我们都知道,栈是一种常用的数据结构,而在内存的堆栈区域,它的数据保存方式就是按照栈的方式进行存取,我们可以通过它的“堆栈指针”获得处理的直接支持。堆栈指针若向下移,会创建新的内存;若向上移,则会释放那些内存。创建程序时,Java 编译器必须准确地知道堆栈内保存的所有数据的“长度”以及“存在时间”。这是由于它必须生成相应的代码,以便向上和向下移动指针。这一限制无疑影响了程序的灵活性,想象一下,在我们创建对象时,很多字段都是动态创建的,所以尽管有些Java数据要保存在堆栈里——特别是对象引用,但实际的对象并不放到其中。

    图2 栈

(3) 。一种常规用途的内存池(也在RAM 区域),java对象都会保存在该位置。我们都知道,“堆”同时也是一个基本的数据结构,它是一种特殊的完全二叉树,分为大顶堆和小顶堆,堆在满足一定的存取效率的基础上,提高了查找效率。和堆栈不同,堆是牺牲了存储效率获取了灵活性,“内存堆”或“堆”(Heap)最吸引人的地方在于编译器不必知道要从堆里分配多少存储空间,也不必知道存储的数据要在堆里停留多长的时间,这也是java对象之所以放置在堆中的原因。进一步讲,正是因为我们有了堆这个结构,java才能够做到方便的创建对象以及创建对象时动态的创建对象的字段。要求创建一个对象时,只需用new命令编制相关的代码即可。执行这些代码时,会在堆里自动进行数据的保存。当然,为达到这种灵活性,必然会付出一定的代价:在堆里分配存储空间时会花掉更长的时间!所以在程序的编写中,我们要尽量避免频繁的创建对象,这会大大降低程序运行的时间。

        图3 大顶堆

(4) 静态存储。这儿的“静态”(Static)是指“位于固定位置”(尽管也在RAM 里)。程序运行期间,静态存储的数据将随时等候调用。我们知道,static可以用来修饰一个字段或者方法是属于一个类的,这个时候static修饰的字段就是静态的,因为它在整个的程序运行过程中都是大小已知的。程序中的常量也存放在这里。

(5) 常数存储。常数值通常直接置于程序代码内部。这样做是安全的,因为它们永远都不会改变。有的常数需要严格地保护,所以可考虑将它们置入只读存储器(ROM)。

(6) 非RAM 存储。若数据完全独立于一个程序之外,则程序不运行时仍可存在,并在程序的控制范围之外。其中两个最主要的例子便是“流式对象”和“固定对象”。对于流式对象,对象会变成字节流,通常会发给另一台机器,比如说网络通信的数据。而对于固定对象,对象保存在磁盘中,比如说图片、数据库中的数据。即使程序中止运行,它们仍可保持自己的状态不变。对于这些类型的数据存储,一个特别有用的技巧就是它们能存在于其他媒体中,比如说采用数据库管理响应的数据。一旦需要,能将它们恢复成普通的、基于RAM 的对象。

常见的数据存放位置示例

接下来,我们来看一下在编写程序中我们经常用到的数据,掌握他们的存放位置,会让我们在编写程序时更加得心应手,更好的把握程序耗费的时间。

在上面的六大存储地点中,寄存器是我们无法直接去控制的,非RAM存储一般会交由其他第三方去控制,因此在这里,我们主要探究剩下的几种方式。

首先我们来看下面的代码:

 public class Person {

     public static final double PI = 3.14;
private String name = "dotgua"; public static void main(String[] args) {
int i = 5;
double d = 0.2;
Person p = new Person();
String str = new String("hello");
String str2 = "world";
}
}

上面这段代码比较短,但是涵盖了我们通常使用的大部分情景。

  • 首先,i是main方法中的局部变量,所以i的值会保存在main的方法栈中(如图所示),除了整型,其他的局部变量,同时是基本类型(boolean/char/double/float等)的都会保存在堆栈中。
  • 然后,p本身是引用类型,引用类型本身也会保存在堆栈中,而p中存放的是它指向的对象的地址(‘#####’部分)。
  • p指向的Person对象会保存在堆中,任何通过new产生的对象都会放置在堆中,对象的成员属性和对象一起存放。请注意,Person类中的PI不会随对象一起存放,因为它本身由static修饰,所以是类成员,会被放置在静态存储中,这里如果有多个Person对象,那么这些对象都会指向同一个PI。
  • 最后,比较有意思的是String类型,如果String是通过new产生的,那么它会和普通的对象一样存放在堆中。而如果是通过字符串字面量声明的,那么,字面量会存放在静态存储中(字面量本身是不会改变的),通过str2引用指向它。字符串放在静态存储中可以方便复用,因为字符串本身是不会改变的,所以如果有多个字符串声明的值相同,其实他们指向的是同一个字符串。这里对于字符串有个小技巧,对于通过new创建的字符串,我们可以使用String类的intern()方法将它重新放置在静态存储中。

总结

在java中,程序运行时,可能存放的位置大致有以下六个,分别是:

  • 寄存器:位于CPU,速度最快,无法控制;
  • 堆栈:内存RAM的一部分,用于存放局部变量;
  • :RAM中的一部分,通过new创建的对象都放在堆中,效率较低;
  • 静态存储:类成员以及常量存放在这里,一般变化较小;
  • 常数存储:常数值会直接存放在程序代码中;
  • 非RAM存储:其他存储方式,不放置在内存中;

字符串通过new方式创建时会存放在堆中,通过字面量创建会存放在静态存储部分,可以通过String类的intern方法将new创建的字符串重新放置在静态存储中。

[java]我的数据在哪里?——数据的内存模型的更多相关文章

  1. 《java并发编程实战》读书笔记13--Java内存模型,重排序,Happens-Before

    第16章 Java内存模型 终于看到这本书的最后一章了,嘿嘿,以后把这本书的英文版再翻翻.这本书中尽可能回避了java内存模型(JMM)的底层细节,而将重点放在一些高层设计问题,例如安全发布,同步策略 ...

  2. JVM运行时数据区与JVM堆内存模型小结

    前提 JVM运行时数据区和JVM内存模型是两回事,JVM内存模型指的是JVM堆内存模型. 那JVM运行时数据区又是什么? 它包括:程序计数器.虚拟机栈.本地方法栈.方法区.堆. 来看看它们都是干嘛的 ...

  3. Java虚拟机学习(1):体系结构 内存模型

    一:Java技术体系模块图 Java技术体系模块图 二:JVM内存区域模型 1.方法区 也称"永久代" ."非堆",  它用于存储虚拟机加载的类信息.常量.静态 ...

  4. 深入理解java虚拟机学习笔记(一)JVM内存模型

    上周末搬家后,家里的宽带一直没弄好,跟电信客服反映了N遍了终于约了个师傅明天早上来迁移宽带,可以结束一个多星期没网的痛苦日子了.这段时间也是各种忙,都一个星期没更新博客了,再不写之前那种状态和激情都要 ...

  5. Java内存模型深度解析:总结--转

    原文地址:http://www.codeceo.com/article/java-memory-7.html 处理器内存模型 顺序一致性内存模型是一个理论参考模型,JMM和处理器内存模型在设计时通常会 ...

  6. 浅析java内存模型--JMM(Java Memory Model)

    在并发编程中,多个线程之间采取什么机制进行通信(信息交换),什么机制进行数据的同步? 在Java语言中,采用的是共享内存模型来实现多线程之间的信息交换和数据同步的. 线程之间通过共享程序公共的状态,通 ...

  7. java内存模型-总结

    处理器内存模型 顺序一致性内存模型是一个理论参考模型,JMM 和处理器内存模型在设计时通常会把顺序一致性内存模型作为参照.JMM 和处理器内存模型在设计时会对顺序一致性模型做一些放松,因为如果完全按照 ...

  8. Java锁(一)之内存模型

    想要了解Java锁机制.引发的线程安全问题以及数据一致性问题,有必要了解内存模型,机理机制了解清楚了,这些问题也就应声而解了. 一.主内存和工作内存 Java内存模型分为主内存和工作内存,所有的变量都 ...

  9. 深入理解Java内存模型(七)——总结

    处理器内存模型 顺序一致性内存模型是一个理论参考模型,JMM和处理器内存模型在设计时通常会把顺序一致性内存模型作为参照.JMM和处理器内存模型在设计时会对顺序一致性模型做一些放松,因为如果完全按照顺序 ...

  10. 【转】深入理解Java内存模型(七)——总结

    处理器内存模型 顺序一致性内存模型是一个理论参考模型,JMM和处理器内存模型在设计时通常会把顺序一致性内存模型作为参照.JMM和处理器内存模型在设计时会对顺序一致性模型做一些放松,因为如果完全按照顺序 ...

随机推荐

  1. MFC设置单文档保存格式以及标题

    在使用MFC编写单文档程序时,有时候需要将编辑的内容序列化为文件,使该文件可以直接以自己的程序打开,这时候需要在保存时将文件后缀改为我们想要的格式. 步骤 打开String Table,找到IDR_M ...

  2. 08-jQuery的位置信息

    Query的位置信息跟JS的client系列.offset系列.scroll系列封装好的一些简便api. 一.宽度和高度 获取宽度 .width() 描述:为匹配的元素集合中获取第一个元素的当前计算宽 ...

  3. 使用Linux自带日志滚动工具logrotate滚动redis日志示例

    截至到redis-5.0版本,redis仍然不会自动滚动日志文件,如果不处理则日志文件日积月累越来越大,最终将导致磁盘满告警: # ls -lh total 12G -rw-r--r-- 1 redi ...

  4. pwm互补输出 死区设置

    void TIM8_PWM_Init(u16 arr,u16 psc){       GPIO_InitTypeDef GPIO_InitStructure;    TIM_TimeBaseInitT ...

  5. CentOS配置多公网

      最终目标是同一台服务器可以多个IP地址共同访问,在这个前提下又有如下两种方式: 多个公网IP使用同一个网关 多个公网IP使用不同网关   这两种方式区别所在:1.多个公网IP使用同一个网关,我们只 ...

  6. ASP.NET MVC下使用AngularJs语言(六):获取下拉列表的value和Text

    前面Insus.NET有在Angularjs实现DropDownList的下拉列表的功能.但是没有实现怎样获取下拉列表的value和text功能. 下面分别使用ng-click和ng-change来实 ...

  7. redis复习

    一起学习...

  8. css高级选择器&盒模型

    css高级选择器&盒模型 1.组合选择器 群组选择器 /* 每个选择器为可以为三种基础选择器的任意一个,用逗号隔开,控制多个*/ div,.div,#div{ color:red } 后代(子 ...

  9. Spring boot 参数相关注解

    最近使用swagger的在线文档调试接口时发现老是报参数问题,最后发现是方法中参数上的注解有问题,今天把填的坑做一下总结. 1. RequestParam 该注解有两个属性: name/value:表 ...

  10. ORACLE更新数据时如果有就更新没有就插入

    SQL写法: begin update table_name set salary = 10000 where emp_id = 5; if sql%notfound then insert into ...