`
jian0487
  • 浏览: 97101 次
  • 性别: Icon_minigender_1
  • 来自: 宁德
社区版块
存档分类
最新评论

java内存的思考与总结

阅读更多

1. java中堆与栈

在Java程序运行时,有6个地方可以用于保存数据:
(1) 寄存器。最快的保存区域,位于处理器内部,数量十分有限,它是根据需要由编译器分配。我们对此没有直接的控制权.

(2) 栈(stack)。驻留于常规RAM(随机访问存储器)区域,这是一种特别快、特别有效的数据保存方式,仅次于寄存器。创建程序时,Java编译器必须准确地知道堆栈内保存的所有数据的“长度”以及“存在时间”。这失去了一定的灵活性,因此对象句柄是存放在栈中,但Java对象并不放到其中。

(3) 堆(heap)。保存了Java对象。和栈不同,它最吸引人的地方在于编译器不必知道要从堆里分配多少存储空间,也不必知道存储的数据要在堆里停留多长的时间。因此,用堆保存数据时会得到更大的灵活性。要求创建一个对象时,只需用new命令编制相关的代码即可。执行这些代码时,会在堆里自动进行数据的保存。当然,为达到这种灵活性,必然会付出一定的代价:在堆里分配存储空间时会花掉更长的时间!

(4) 静态存储。这儿的“静态”(Static)是指“位于固定位置”(尽管也在RAM里)。程序运行期间,静态存储的数据将随时等候调用。可用static关键字指出一个对象的特定元素是静态的。但Java对象本身永远都不会置入静态存储空间。

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

(6) 非RAM存储。数据完全独立于一个程序之外,则程序不运行时仍可存在,并在程序的控制范围之外。

2.堆和栈的区别

栈与堆都是Java用来在Ram中存放数据的地方。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。

Java 的堆是一个运行时数据区,类的(对象从中分配空间。这些对象通过new、newarray、anewarray和multianewarray等指令建立,它们不需要程序代码来显式的释放。堆是由垃圾回收来负责的,堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。

栈的优势是,存取速度比堆要快,仅次于寄存器,栈数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。栈中主要存放一些基本类型的变量(,int, short, long, byte, float, double, boolean, char)和对象句柄。

栈有一个很重要的特殊性,就是存在栈中的数据可以共享。假设我们同时定义:
int a = 3;
int b = 3;
编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找栈中是否有3这个值,如果没找到,就将3存放进来,然后将a指向3。接着处理int b = 3;在创建完b的引用变量后,因为在栈中已经有3这个值,便将b直接指向3。这样,就出现了a与b同时均指向3的情况。

这时,如果再令a=4;那么编译器会重新搜索栈中是否有4值,如果没有,则将4存放进来,并令a指向4;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。

要注意这种数据的共享与两个对象的引用同时指向一个对象的这种共享是不同的,因为这种情况a的修改并不会影响到b, 它是由编译器完成的,它有利于节省空间。而一个对象引用变量修改了这个对象的内部状态,会影响到另一个对象引用变量。

String是一个特殊的包装类数据。可以用:
String str = new String("abc");
String str = "abc";
两种的形式来创建,第一种是用new()来新建对象的,它会在存放于堆中。每调用一次就会创建一个新的对象。
而第二种是先在栈中创建一个对String类的对象引用变量str,然后查找栈中有没有存放"abc",如果没有,则将"abc"存放进栈,并令str指向”abc”,如果已经有”abc” 则直接令str指向“abc”。

比较类里面的数值是否相等时,用equals()方法;当测试两个包装类的引用是否指向同一个对象时,用==,下面用例子说明上面的理论。
String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2); //true
可以看出str1和str2是指向同一个对象的。

String str1 =new String ("abc");
String str2 =new String ("abc");
System.out.println(str1==str2); // false
用new的方式是生成不同的对象。每一次生成一个。


因此用第二种方式创建多个”abc”字符串,在内存中其实只存在一个对象而已. 这种写法有利与节省内存空间. 同时它可以在一定程度上提高程序的运行速度,因为JVM会自动根据栈中数据的实际情况来决定是否有必要创建新对象。而对于String str = new String("abc");的代码,则一概在堆中创建新对象,而不管其字符串值是否相等,是否有必要创建新对象,从而加重了程序的负担。

另一方面, 要注意: 我们在使用诸如String str = "abc";的格式定义类时,总是想当然地认为,创建了String类的对象str。担心陷阱!对象可能并没有被创建!而可能只是指向一个先前已经创建的对象。只有通过new()方法才能保证每次都创建一个新的对象。
由于String类的immutable性质,当String变量需要经常变换其值时,应该考虑使用StringBuffer类,以提高程序效率。

参考文献:
关于Java栈与堆的思考
http://www.duduwolf.com/post/3.asp

第2章 一切都是对象
http://52cg.com/Training/KaiFa/java/200510/31900.html


3.关于java中数据占用内存空间的大小

――测试环境
j2me midp1.0
――测试方法:
使用下面的语句,获取对象创建前后的内存值,它们的差值就为所创建对象占用的内存大小:
1.i = Runtime.getRuntime().freeMemory();
2.control = new Control(Control.HEAD_MOVE, 1, 1);
3.j = Runtime.getRuntime().freeMemory();
4.System.out.println(i-j);
这个方法并不准确,因为JVM会不定时在后台运行一些东西,比如在gc,后台运行东西会占用一定的内存,如果这个后台程序的运行发生在1、3句之间的时候就会造成不准确。

为了尽可能保持正确性,将语句变为如下,先gc,再取内存,让那些由于后台运行而占用的内存释放出来。
Runtime.getRuntime().gc();
Thread.yield();
i = Runtime.getRuntime().freeMemory();
test t = new test();
Runtime.getRuntime().gc();
Thread.yield();
j = Runtime.getRuntime().freeMemory();
System.out.println(i-j);
但是这种方法还是不一定能够保证gc会被调用,因此每一个测试得到的数据都是多次测试得出相同的那个数据,取当堆大小稳定以后显示的数据,追求尽量准确。


(1) i = Runtime.getRuntime().freeMemory();
int t = 0;
char a ='a';
long l =23410389;
j = Runtime.getRuntime().freeMemory();
System.out.println(i-j); // 0
【结论】因为基本数据类型存放在栈中,并不占用堆的内存,可见freeMemory返回的数值是堆中的内存,因此值为0。

(2)
i = Runtime.getRuntime().freeMemory();
String s2="1234567";
j = Runtime.getRuntime().freeMemory();
System.out.println(i-j); // 0
―――――――――――――――――――――――――――――――
i = Runtime.getRuntime().freeMemory();
String s2=new String("1234567");
j = Runtime.getRuntime().freeMemory();
System.out.println(i-j); // 56

【结论】以String s2="1234567"方式创建的字符串是存放在栈中,它不占用堆空间;而用String s2=new String("1234567");方式创建的字符串是存放在堆中,它占用了56byte的堆空间。

(3)
Runtime.getRuntime().gc();
Thread.yield();
i = Runtime.getRuntime().freeMemory();
test t = new test(); //test为一个空的Class
Runtime.getRuntime().gc();
Thread.yield();
j = Runtime.getRuntime().freeMemory();
System.out.println(i-j); // 12

――――――――――――――――――――――――――――――――
Runtime.getRuntime().gc();
Thread.yield();
i = Runtime.getRuntime().freeMemory();
Contorl c = new Control(Control.HEAD_MOVE, 1, 1);
Runtime.getRuntime().gc();
Thread.yield();
j = Runtime.getRuntime().freeMemory();
System.out.println(i-j); //112

【结论1】:一个空的对象需要占用12byte的堆空间,将其理解为对象头。
【结论2】:一个zhanguo/Control的一个“HEAD_MOVE”对象占用112byte堆空间。

为何是112byte?
Control中有25个非static的基本数据类型和引用变量,而int,short,byte等基本数据类型和引用变量占用的内存大小都是4byte(详见4),因此大小为:
25*4 + 12 (对象头)==112


(4)在上面的Class test中加入: private int a = 0;
得出结果:16byte ,
由(3)可以知空object占12byte,所以一个int占用4byte的空间。
将int改为其他基本数据类型,用同样方法进行测试,得出如下结论:
【结论1】:float, boolean,byte, short, int, char占用4个byte的空间,
long,double占用8byte空间 (midp1.0不支持float和double)
引用变量,比如 Control c = null, 占用4byte空间。

【结论2】:在java中类型决定行为,而不是大小;用byte起到限制数据的作用,但是并不能节约内存,在内存中byte和int一样是占用4byte的空间。

【结论3】:虽然a为基本的数据类型,但是它是对象t的一个属性,是在运行时才动态创建的,因此它也是存放在堆中,占用4byte的堆空间。


(5)在Class test中加入下面语句:并去掉(4)中加入的变量 int a。
private final static int j = 1;
private final static int k = 4;
private static void testFunction(){
}
得出结果:12
【结论1】一个object的占用堆空间的多少只与类中的非static的基本数据类型和引用变量有关,而与static的变量和function的多少并没有关系。Static是存放在“静态存储空间”,不占用堆空间。

【结论2】对于需要生成多个object的类,比如control等,类中的成员变量尽量用static,因为object中的非staitc变量在堆中需要占用一定的空间,当object数量比较多时,占用的堆空间会增大很多。从而容易出现内存不足(主要是堆空间不足)造成“应用程序错误”的情况。

(6)
i = Runtime.getRuntime().freeMemory();
Strin s=new String("");
j = Runtime.getRuntime().freeMemory();
System.out.println(i-j); //40
【结论】一个空的String就要占用40字节的堆空间,理解为String头信息,在代码中要避免反复得new一个相同的string,而是应该利用上面(2)中所讲的那种方式:
String s2="1234567"来生成String。


(7)
Runtime.getRuntime().gc();
Thread.yield();
i = Runtime.getRuntime().freeMemory();
int a[][] = new int[0][0];
Runtime.getRuntime().gc();
Thread.yield();
j = Runtime.getRuntime().freeMemory();
System.out.println(i-j); //16
----------------------------------------------------------------------------------
int a[][] = new int[1][0]; //36
int a[][] = new int[2][0]; //56
int a[][] = new int[3][0]; //76
int a[][] = new int[200][0]; //4016
int a[][] = new int[200][1]; //4816 ,因为比上面多了200个int,所以多了800

【结论1】每一个嵌套的int[dim2]都是一个对象,一个int占用4byte空间,每一个对象都有一个16字节的数组对象头。
【结论2】二维数组的数组头的内存消耗很大,对于对内存较小的手机,比如nokia旧40系列,最好将二维数组改为用一维数组。

参考文章:
Discover how much memory an object consumes
http://www.mywelt.net/?q=node/577

你知道数据大小吗?-不要花太多的功夫来隐藏类的成员
http://istudy.com.ru/Article/Software/Java/20051025/54,81897,0.html


TODO:
1. 关于类,变量,函数等与jar大小的关系,考虑混淆器的作用,对于混淆器可以做到的就不必在程序中去改变。
2. 关于j2me程序与内存的关系进一步深化,比如线程,gc等等。

???
1.Java程序编译后编程*.class文件,那么在运行时这些文件是一次性加载进内存的?如此程序代码很长的话,光是存放这些代码就要消耗大量的内存?它们是存放在内存的什么地方?上面提到的几个地方明显都不是存放代码的地方。

分享到:
评论

相关推荐

    java面试笔试题库java软件设计java笔试题大集合及答案文档资料合集300MB.zip

    Java内存模型的历史变迁.docx Java在游戏服务器开发中的应用.docx java基础总结大全.txt Java开发与技术挑战——关于技术的技术思考.docx Java框架研发思考.docx Java程序员们最常犯的10个错误.docx java程序员的...

    java重要基础知识点总结.pdf

    本文将总结Java的重要基础知识点,帮助读者深入理解和掌握这一强大的编程工具。 1. **Java基础语法**: - 类(Class)和对象(Object)是Java的核心概念,类定义了对象的属性和行为。 - 引用数据类型包括类、接口...

    关于Java栈与堆的思考

    关于Java栈与堆的深入解析 Java作为一种广泛使用的编程语言,其内存管理机制是学习者必须掌握的核心概念之一。在Java中,栈(Stack)与堆(Heap)是用于存储数据的主要区域,它们各自承担着不同的职责,对于理解...

    java面试笔试资料Java经典项目集锦java笔试题大集合及答案题库java笔试题汇总资料个合集(188).zip

    Java内存模型的历史变迁.docx Java在游戏服务器开发中的应用.docx java基础总结大全.txt Java开发与技术挑战——关于技术的技术思考.docx Java框架研发思考.docx Java程序员们最常犯的10个错误.docx java程序员的...

    Java进阶知识点总结

    ### Java进阶知识点总结 #### 一、面向对象思想特点 面向对象编程(OOP)是一种程序设计范式,它的核心思想在于将现实世界中的事物抽象为对象,通过对象的组合来构建复杂的系统。 1. **面向对象的特点**: - **更...

    2022最新Java个人面试总结及整理

    Java作为全球最流行的编程语言之一,其在面试中的地位不言而喻。这份"2022最新Java个人面试总结及整理"包含了开发者在求职...这份"Java面试思考总结"正是为这样的目标而准备,它将帮助你在面试的舞台上更加自信和专业。

    java面试笔试资料java笔试题大集合及答案题库java笔试题汇总资料188个合集.zip

    Java内存模型的历史变迁.docx Java在游戏服务器开发中的应用.docx java基础总结大全.txt Java开发与技术挑战——关于技术的技术思考.docx Java框架研发思考.docx Java程序员们最常犯的10个错误.docx java程序员的...

    java面试笔试题库java学习笔记开发教程互联网公司面试资料大全合集.zip

    Java内存模型的历史变迁.docx Java在游戏服务器开发中的应用.docx java基础总结大全.txt Java开发与技术挑战——关于技术的技术思考.docx Java框架研发思考.docx Java程序员们最常犯的10个错误.docx java程序员的...

    corejava基础重要知识点总结

    面向过程:需要人站在计算机的角度去思考问题 面向对象:需要人拿着代码模拟实现生活 类:一组类型相同事物高度抽象之后的集合概念 创建对象的模板 -》 class 对象:类的一个具体的实例 例子: 人和范冰冰...

    java面试笔试题库java笔试题大集合及答案互联网公司面试资料Java面试问题集大全合集(200个).zip

    Java内存模型的历史变迁.docx Java在游戏服务器开发中的应用.docx java基础总结大全.txt Java开发与技术挑战——关于技术的技术思考.docx Java框架研发思考.docx Java程序员们最常犯的10个错误.docx java程序员的...

    【大厂面试题总结】JavaSE面试题总结详细教程

    【大厂面试题总结】JavaSE面试题总结详细教程: 目录: 递归算法之输出某个目录下所有文件和子目录列表 泛型中extends和super的...JAVA内存泄漏详解 java序列化方式 java中实现多态的机制 string常量池和intern韩雅茹

    java知识点总结大全

    Java知识点总结大全旨在帮助初学者和进阶者深入理解Java编程语言的核心概念和高级特性。以下是对标题和描述中提及的各个知识点的详细说明: 1. **编程**:编程是用特定的语言(如Java)编写指令,让计算机执行任务...

    工作总结之java顶岗实习总结.docx

    本文是对一位Java实习生在顶岗实习期间的学习与工作经验的总结。实习期间,实习生在西安软件服务外包学院进行了为期三个月的专业实践,主要学习了Java、JavaScript以及相关的数据库技术,如Oracle和MySQL,同时也...

    JAVA课程总复习

    11. **JVM内存模型**:解释Java虚拟机的工作原理,包括堆内存、栈内存、方法区、本地方法栈等区域,以及垃圾回收机制。 12. **设计模式**:介绍常见的设计模式,如单例、工厂、观察者、装饰者等,并探讨它们在实际...

    Java框架研发思考.docx

    总结来说,Jdon框架的研发体现了作者对Java开发深入的理解和独特的见解。他尝试通过集成和创新来优化多层架构的复杂性,结合DDD和并发模型提升效率,并运用CAP定理解决分布式环境下的数据一致性问题。这篇文档对于...

    关于C语言指针和java引用的思考.pdf

    关于C语言指针和Java引用的思考 该资源主要讨论了C语言指针和Java引用的概念、实现、差异和应用场景。下面是对该资源的详细分析和知识点总结: 1. C语言指针: C语言中,函数的内存都是分配在栈中的,当该函数...

    专题资料(2021-2022年)java面试题知识点总结.doc

    Java面试题知识点总结 在Java编程中,同步是多线程环境下确保数据一致性的重要机制。当多个线程并发地操作共享资源时,如果没有适当的同步控制,可能会引发数据不准确和线程间的冲突。Java提供了两种主要的同步方式...

    【大厂面试题总结】JavaSE面试题合集及其答案,基本包括javaSE所有知识点和详细解释

    【大厂面试题总结】JavaSE面试题合集及其答案,基本包括javaSE所有知识点和详细解释 。 JavaSE面试题总结详细教程: ...JAVA内存泄漏详解 java序列化方式 java中实现多态的机制 string常量池和intern

    java基础总结

    - **常量与变量**:常量是程序中不会变化的数据,而变量是存储数据的内存空间,其值可以改变。变量定义需要考虑数据类型、变量名和初始化值。变量的作用域是指其可以被访问的代码区域,通常从变量定义的位置开始,...

    Java心得 学JAVA必看

    例如,对于Java中的垃圾回收机制,不仅要学会如何使用相关的API,还应该了解其内部的工作原理,这样才能更好地进行内存管理。 #### 7. 多语言并行学习 虽然专注于Java的学习是必要的,但同时也建议学习其他编程语言...

Global site tag (gtag.js) - Google Analytics