一.、JAVA中的数据存储
在JAVA中,有六个不同的地方可以存储数据:寄存器、栈、堆、静态存储、常量存储、非RAM存储
存储速度:寄存器<栈<堆<其他
二、关于栈
栈是一种先进后出的线性表,存放基本类型的变量数据和对象,数组的引用
1. 栈中的方法调度
a) 嵌套方法
void m1(){
m2();
}
void m2(){
m3();
}
void m3(){
m4();
}
void m4(){
}
当嵌套方法调用时,嵌套越深,stack的内存就越晚才能释放,因此,在实际开发过程中,不推荐大家使用递归来进行方法的调用,递归很容易导致stack flow
b) 非嵌套方法
void m1(){
m2();
m3();
m4();
}
每次方法调用结束时依次释放栈的内存
2.栈溢出
a) 递归调用
public class TestStackOutRecursio {
/**
* 递归调用
*/
public void testRecursion (){
this.testRecursion();
}
public static void main(String[] args){
TestStackOutRecursio t = new TestStackOutRecursio();
t.testRecursion();
}
}
b)大量循环或死循环
public class TestStackOutCircle {
public static void main(String[] args){
Children children = new Children();
children.run();
}
}
class Parent {
private Children children = new Children();
}
class Children extends Parent {
public void run(){
System.out.println("Children");
}
}
在父类中创建子类对象,导致无限循环,最终栈内存溢出
解决方法:检查代码是否有上述原因
三、关于堆
堆是完全二叉树,堆中各元素是有序的。在这个二叉树中所有的双亲节点和孩子节点存在着大小关系。存放所有new出来的对象。
堆溢出:死循环或不必要地重复创建大量对象
import java.util.ArrayList;
import java.util.List;
public class TestHeapOut {
static class Object {
}
public static void main(String[] args) {
List<Object> list = new ArrayList<Object>();
int i= 0;
while (true) {
list.add(new Object());
i++ ;
if(i%40000 == 0){
System.out.println("ing:" + i + " list:"+list.toString().getBytes().length);
}
}
}
}
解决方法:
1)检查代码中是否有有死循环或不必要地重复创建大量对象
2)适当增加Java虚拟机中Xms(初始堆大小)和Xmx(最大堆大小)参数的大小。如:set JAVA_OPTS= -Xms256m -Xmx1024m
四、栈与堆的区别
1. 堆是一个运行时数据区,通过new等指令创建,不需要程序代码显式释放
优点:可动态分配内存大小,生存周期不必事先告诉编译器,Java垃圾回收自动回收不需要的数据;
缺点:运行时需动态分配内存,数据存取速度较慢。
如:
String str = new String("abc");
String str2 = new String("abc");
2. 栈限制仅在表的一端进行插入和删除运算的线性表,先进后出FILO
优点:存取速度比堆快,仅次于寄存器,栈数据可以共享
缺点:存在栈中的数据大小与生存期必须是确定的,缺乏灵活性
如:
String str = "abc";
String str2 = "abc";
执行效率测试:摘自 http://blog.csdn.net/qhuwn/article/details/4100049
测试一、
/*
* 测试栈、实例变量、类变量的执行速度
* 用instanceVar和staticVar分别定义实例变量和类变量
* 在getStackTime() 方法体中用了int j=0;来获取一个栈变量
*/
public class ExecuteTimeTest {
int instanceVar; //定义实例变量
static int staticVar; //定义类变量
long stackTime; //存放栈变量运行的时间
long instanceTime; //存放实例变量运行的时间
long staticTime; //存放静态变量运行的时间
//getStackTime()获取栈运算的多次执行时间
long getStackTime(){
long stackStartTime = System.currentTimeMillis();
int j=0;
for(int i=0;i<300000000;i++){
j+=i;
}
long stackEndTime = System.currentTimeMillis();
stackTime=stackEndTime-stackStartTime;
return stackTime;
}
// getInstanceTime()获取实例运算的多次执行时间
long getInstanceTime(){
long instanceStartTime = System.currentTimeMillis();
for(int i=0;i<300000000;i++){
instanceVar+=i;
}
// int j = instanceVar;
// for(int i=0;i<300000000;i++){
// j+=i;
// }
// instanceVar = j;
long instanceEndTime = System.currentTimeMillis();
instanceTime=instanceEndTime-instanceStartTime;
return instanceTime;
}
// getStaticTime()获取类变量运算的多次执行时间
long getStaticTime(){
long staticStartTime = System.currentTimeMillis();
for(int i=0;i<300000000;i++){
staticVar+=i;
}
// int j = staticVar;
// for(int i=0;i<300000000;i++){
// j+=i;
// }
// staticVar = j;
long staticEndTime = System.currentTimeMillis();
staticTime=staticEndTime-staticStartTime;
return staticTime;
}
public static void main(String[] args) {
ExecuteTimeTest executeTimeTest = new ExecuteTimeTest();
System.out.println(executeTimeTest.getStackTime()); //打印栈变量运算的运行时间
System.out.println(executeTimeTest.getInstanceTime()); //打印实例变量运算的运行时间
System.out.println(executeTimeTest.getStaticTime()); //打印类变量运算的运行时间
}
}
运行结果:单位毫秒
271
735
736
总结1:通过测试,可以看出堆栈变量运算的执行时间要远远少于其他两种变量的执行时间,但是堆栈变量对外部代码块几乎是没有什么意义。
测试二:在代码快中将类变量、实例变量的值赋给快中一个堆栈变量
/*
* 测试栈、实例变量、类变量的执行速度
* 用instanceVar和staticVar分别定义实例变量和类变量
* 在getStackTime() 方法体中用了int j=0;来获取一个栈变量
*/
public class ExecuteTimeTest {
int instanceVar; //定义实例变量
static int staticVar; //定义类变量
long stackTime; //存放栈变量运行的时间
long instanceTime; //存放实例变量运行的时间
long staticTime; //存放静态变量运行的时间
//getStackTime()获取栈运算的多次执行时间
long getStackTime(){
long stackStartTime = System.currentTimeMillis();
int j=0;
for(int i=0;i<300000000;i++){
j+=i;
}
long stackEndTime = System.currentTimeMillis();
stackTime=stackEndTime-stackStartTime;
return stackTime;
}
// getInstanceTime()获取实例运算的多次执行时间
long getInstanceTime(){
long instanceStartTime = System.currentTimeMillis();
// for(int i=0;i<300000000;i++){
// instanceVar+=i;
// }
int j = instanceVar;
for(int i=0;i<300000000;i++){
j+=i;
}
// instanceVar = j;
long instanceEndTime = System.currentTimeMillis();
instanceTime=instanceEndTime-instanceStartTime;
return instanceTime;
}
// getStaticTime()获取类变量运算的多次执行时间
long getStaticTime(){
long staticStartTime = System.currentTimeMillis();
// for(int i=0;i<300000000;i++){
// staticVar+=i;
// }
int j = staticVar;
for(int i=0;i<300000000;i++){
j+=i;
}
staticVar = j;
long staticEndTime = System.currentTimeMillis();
staticTime=staticEndTime-staticStartTime;
return staticTime;
}
public static void main(String[] args) {
ExecuteTimeTest executeTimeTest = new ExecuteTimeTest();
System.out.println(executeTimeTest.getStackTime()); //打印栈变量运算的运行时间
System.out.println(executeTimeTest.getInstanceTime()); //打印实例变量运算的运行时间
System.out.println(executeTimeTest.getStaticTime()); //打印类变量运算的运行时间
}
}
运算结果:单位毫秒
257
250
251
总结2:通过测试的结果可以看出,将定义好的实例变量和类变量在代码块中赋值给一个堆栈变量的话,可以大量提升运算速率。