`
luzl
  • 浏览: 563854 次
  • 性别: Icon_minigender_1
  • 来自: 大连
社区版块
存档分类
最新评论

Java变量作用域内存

    博客分类:
  • Java
阅读更多
我一直在想一个问题,做循环的时候变量在循环体内定义省内存还是变量在循环体外定义省内存,于是做了下实验:
import junit.framework.TestCase;

class HoldMemory{
	String str01="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	
	public HoldMemory(){
	}
}

public class MemoryCase extends TestCase{

	public void testname() throws Exception {
		for (int i = 0; i < 50000; i++) {
			HoldMemory holdMemory=new HoldMemory();
		}
		System.out.println(Runtime.getRuntime().freeMemory());
		
	}
}


和下面的code进行比较:
import junit.framework.TestCase;

class HoldMemory{
	String str01="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	
	public HoldMemory(){
	}
}

public class MemoryCase extends TestCase{

	public void testname() throws Exception {
                HoldMemory holdMemory=null;
		for (int i = 0; i < 50000; i++) {
		     holdMemory=new HoldMemory();
		}
		System.out.println(Runtime.getRuntime().freeMemory());
		
	}
}

结果两个打印的结果是一样的,当然我是分两次运行的,不是在一一起运行的。所以感觉没有区别,可能从作用域来说作用域越小,越容易被回收吧,所以并不是变量定义在外面科学,感觉应该是定义在里面更好一些,因为生命周期短,容易被回收。
但是从CPU角度讲,可能写在外面的话,创建新变量的过程省了,是不是更加节省CPU执行单元?

另外有以下代码可以跑着看看内存什么时候被回收,你可以试试写在外面好还是写在里面好,我跑着试了试,感觉都一样:
package com.ibm.partnerworld.vic.testcase;

import junit.framework.TestCase;

class HoldMemory{
	String str01="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str02="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str03="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str04="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str05="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str06="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str07="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str08="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str09="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str10="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str11="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str12="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str13="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str14="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str15="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	
	public HoldMemory(){
		try {
			System.out.println("Created");
			Thread.currentThread().sleep(3);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	@Override
	protected void finalize() throws Throwable {
		// TODO Auto-generated method stub
		super.finalize();
		System.out.println("I was reused!");
	}
}

public class MemoryCase extends TestCase{

	public void testname() throws Exception {
		HoldMemory holdMemory=null;
		for (int i = 0; i < 50000; i++) {
			holdMemory=new HoldMemory();
			System.out.println("total=>"+Runtime.getRuntime().totalMemory());
			System.out.println("free=>"+Runtime.getRuntime().freeMemory());
		}
		System.out.println(Runtime.getRuntime().freeMemory());
		
	}
}

分享到:
评论
22 楼 sswh 2010-05-23  
这儿再做一个简单的试验,看一下:

public class Hello {
	void testMemory() throws Exception {
		logMemoryInfo();
		for (int i = 0; i < 1; i++) {
			Object ref = new int[500000];
		}
		logMemoryInfo();
	}

	void logMemoryInfo() throws Exception {
		System.out.print("before gc: usedmem: ");
		System.out.println(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory());
		System.gc();
		Thread.sleep(3000);
		System.out.print("after gc: usedmem: ");
		System.out.println(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory());
	}

	public static void main(String[] args) throws Exception {
		new Hello().testMemory();
	}
}


在我的机子上,运行结果如下:
before gc: usedmem: 215616
after gc: usedmem: 155336
before gc: usedmem: 2155352
after gc: usedmem: 137008

把上面的代码改成:
void testMemory() throws Exception {
	logMemoryInfo();
	Object ref;
	for (int i = 0; i < 1; i++) {
		ref = new int[500000];
	}
	logMemoryInfo();
}


运行结果不变。
21 楼 sswh 2010-05-23  
zl198751 写道


所谓字节码完全相同而位置不一定,就说程序是一致的,我看很大程度上是不对的。
就是因为变量i和holdMemory在局部变量表的位置不同,所以程序在运行时的内存状态是不一致的。

当holdMemory定义在外面的话,循环结束后内存中依然保留为holdMemory开辟的内存。但是定义在里面的话内存在循环结束后就被回收了。


对于JVM而言,没有块级别的作用域,在进入方法体时,局部变量所占用的内存就已经在Java栈上分配好了,
直到方法结束,Java栈上的局部变量内存才被回收,不存在方法执行途中回收的问题。

不过要注意,这儿的内存仅仅是指Java栈上的内存(对于对象而言,也就是非primitive的变量,仅仅是对象的引用)
而Java对象所分配的内存,是位于Java堆中。

就楼主的例子,不论holdMemory定义在循环外、或者循环内,都不影响每一次循环中创建的new HoldMemory()的回收,因为,每一次循环的执行,都重写了同一个变量引用,从而导致上一次循环holdMemory对象引用的丢失,即原则上,每开始一个新的循环,上一个循环创建的holdMemory对象都是可回收的,这和holdMemory变量定义在循环体内外无关。



~~~~~~~~~~~~~~~~~~~~~~~
小结一下:
1、Java栈上的局部变量(primitive值、或者对象引用)所占用的内存,在方法结束后,随Java栈的回收而回收,与变量定义在循环体内外无关;
2、Java对象(使用堆内存)在每次循环后,自动丢弃上一次循环所保留的对象引用,从而该对象处于可回收状态。这也和变量定义在循环体内外无关;
3、局部变量定义在循环体内外,仅仅对代码的可读性有影响。对代码执行没有区别。
20 楼 zl198751 2010-05-13  
sswh 写道
另外,你的第二段代码如果去掉holdMemory初始化为null操作,

也就是下面两种情况:
	public void testname() throws Exception {
		HoldMemory holdMemory;//去掉=null操作
		for (int i = 0; i < 50000; i++) {
			holdMemory = new HoldMemory();
		}
		System.out.println(Runtime.getRuntime().freeMemory());
	}



	public void testname() throws Exception {
		for (int i = 0; i < 50000; i++) {
			HoldMemory holdMemory = new HoldMemory();
		}
		System.out.println(Runtime.getRuntime().freeMemory());
	}


所生成的字节码是完全相同的。(只是变量i和holdMemory在局部变量表的位置不同,对程序运行没有任何影响)

可见:对Java而言,局部变量声明在循环体内、或者循环体外没有任何区别。



所谓字节码完全相同而位置不一定,就说程序是一致的,我看很大程度上是不对的。
就是因为变量i和holdMemory在局部变量表的位置不同,所以程序在运行时的内存状态是不一致的。

当holdMemory定义在外面的话,循环结束后内存中依然保留为holdMemory开辟的内存。但是定义在里面的话内存在循环结束后就被回收了。
19 楼 gml520 2010-05-13  
sswh 写道
lbfhappy 写道
楼上查看字节码的工具是什么啊.能介绍一下不?
看上去好像是netbeans的感觉啊


是jclasslib


我用jclasslib 写了一个 NetBeans 插件。http://www.iteye.com/topic/665511
18 楼 feigme 2010-05-13  
sswh 写道
另外,你的第二段代码如果去掉holdMemory初始化为null操作,

也就是下面两种情况:
	public void testname() throws Exception {
		HoldMemory holdMemory;//去掉=null操作
		for (int i = 0; i < 50000; i++) {
			holdMemory = new HoldMemory();
		}
		System.out.println(Runtime.getRuntime().freeMemory());
	}



	public void testname() throws Exception {
		for (int i = 0; i < 50000; i++) {
			HoldMemory holdMemory = new HoldMemory();
		}
		System.out.println(Runtime.getRuntime().freeMemory());
	}


所生成的字节码是完全相同的。(只是变量i和holdMemory在局部变量表的位置不同,对程序运行没有任何影响)

可见:对Java而言,局部变量声明在循环体内、或者循环体外没有任何区别。


同意sswh
在运行时,声明根本不会执行
所以定义在循环外或内没有什么区别
不过,还是遵守最小作用域原则
17 楼 opmic 2010-05-12  
flyfan 写道
lbfhappy 写道
楼上查看字节码的工具是什么啊.能介绍一下不?
看上去好像是netbeans的感觉啊

强烈要求公布一下

16 楼 icyiwh 2010-05-12  
  在外面不要初始化(哪怕是null)
15 楼 szmq2 2010-05-11  
我认为编程工作和做事的方法是一样的,想了一个不算恰当的比方:
    会议室需要招开10次不同的会议,每次会议完后你需要清理会场
    以下设备其实是不用重复清理的:座子、椅子(没必要每次开完会后把座子椅子拿回去,然后又重新摆好)
   以下设备其实是需要每次清理的:一次性水杯(这些不能给其它到会人员重复使用吧)
14 楼 my13901 2010-05-11  
引:Java是编译型语言不是解释型语言。

我晕,Java不是编译型语言是解释型语言。
13 楼 zxsy007x 2010-05-11  
谢谢 学习了
12 楼 prowl 2010-05-11  
字节码工具很nice  多谢sswh了
11 楼 sswh 2010-05-11  
另外,你的第二段代码如果去掉holdMemory初始化为null操作,

也就是下面两种情况:
	public void testname() throws Exception {
		HoldMemory holdMemory;//去掉=null操作
		for (int i = 0; i < 50000; i++) {
			holdMemory = new HoldMemory();
		}
		System.out.println(Runtime.getRuntime().freeMemory());
	}



	public void testname() throws Exception {
		for (int i = 0; i < 50000; i++) {
			HoldMemory holdMemory = new HoldMemory();
		}
		System.out.println(Runtime.getRuntime().freeMemory());
	}


所生成的字节码是完全相同的。(只是变量i和holdMemory在局部变量表的位置不同,对程序运行没有任何影响)

可见:对Java而言,局部变量声明在循环体内、或者循环体外没有任何区别。
10 楼 sswh 2010-05-11  
lbfhappy 写道
楼上查看字节码的工具是什么啊.能介绍一下不?
看上去好像是netbeans的感觉啊


是jclasslib
9 楼 J-catTeam 2010-05-11  
luzl 写道
我一直在想一个问题,做循环的时候变量在循环体内定义省内存还是变量在循环体外定义省内存,于是做了下实验:
import junit.framework.TestCase;

class HoldMemory{
	String str01="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	
	public HoldMemory(){
	}
}

public class MemoryCase extends TestCase{

	public void testname() throws Exception {
		for (int i = 0; i < 50000; i++) {
			HoldMemory holdMemory=new HoldMemory();
		}
		System.out.println(Runtime.getRuntime().freeMemory());
		
	}
}


和下面的code进行比较:
import junit.framework.TestCase;

class HoldMemory{
	String str01="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	
	public HoldMemory(){
	}
}

public class MemoryCase extends TestCase{

	public void testname() throws Exception {
                HoldMemory holdMemory=null;
		for (int i = 0; i < 50000; i++) {
		     holdMemory=new HoldMemory();
		}
		System.out.println(Runtime.getRuntime().freeMemory());
		
	}
}

结果两个打印的结果是一样的,当然我是分两次运行的,不是在一一起运行的。所以感觉没有区别,可能从作用域来说作用域越小,越容易被回收吧,所以并不是变量定义在外面科学,感觉应该是定义在里面更好一些,因为生命周期短,容易被回收。
但是从CPU角度讲,可能写在外面的话,创建新变量的过程省了,是不是更加节省CPU执行单元?

另外有以下代码可以跑着看看内存什么时候被回收,你可以试试写在外面好还是写在里面好,我跑着试了试,感觉都一样:
package com.ibm.partnerworld.vic.testcase;

import junit.framework.TestCase;

class HoldMemory{
	String str01="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str02="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str03="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str04="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str05="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str06="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str07="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str08="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str09="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str10="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str11="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str12="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str13="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str14="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str15="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	
	public HoldMemory(){
		try {
			System.out.println("Created");
			Thread.currentThread().sleep(3);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	@Override
	protected void finalize() throws Throwable {
		// TODO Auto-generated method stub
		super.finalize();
		System.out.println("I was reused!");
	}
}

public class MemoryCase extends TestCase{

	public void testname() throws Exception {
		HoldMemory holdMemory=null;
		for (int i = 0; i < 50000; i++) {
			holdMemory=new HoldMemory();
			System.out.println("total=>"+Runtime.getRuntime().totalMemory());
			System.out.println("free=>"+Runtime.getRuntime().freeMemory());
		}
		System.out.println(Runtime.getRuntime().freeMemory());
		
	}
}


在外面·那么只有一个引用
里面·引用的个数就是循环的次数
8 楼 flyfan 2010-05-11  
lbfhappy 写道
楼上查看字节码的工具是什么啊.能介绍一下不?
看上去好像是netbeans的感觉啊

强烈要求公布一下
7 楼 luzl 2010-05-10  
sswh 写道
luzl 写道
我一直在想一个问题,做循环的时候变量在循环体内定义省内存还是变量在循环体外定义省内存,于是做了下实验:
	public void testname() throws Exception {
		for (int i = 0; i < 50000; i++) {
			HoldMemory holdMemory=new HoldMemory();
		}
		System.out.println(Runtime.getRuntime().freeMemory());
		
	}
}


和下面的code进行比较:
	public void testname() throws Exception {
                HoldMemory holdMemory=null;
		for (int i = 0; i < 50000; i++) {
		     holdMemory=new HoldMemory();
		}
		System.out.println(Runtime.getRuntime().freeMemory());
		
	}
}


Java是编译型语言不是解释型语言。
上面两段代码生成的字节码,对于方法testname() 而言,其局部变量都是3个:this、i、holdMemory,
即使看上去“HoldMemory holdMemory”是在循环体内声明的,holdMemory变量也仍然只有一个。

这两段代码生成的字节码的唯一区别是 第一段代码前面多了局部变量的初始化操作。

第一段代码对应的字节码:


第二段代码对应的字节码:



用你这个就很好理解为什么两个在内存的占用上是一样的了,请教一下你是怎么看字节码的?
6 楼 lbfhappy 2010-05-10  
楼上查看字节码的工具是什么啊.能介绍一下不?
看上去好像是netbeans的感觉啊
5 楼 sswh 2010-05-10  
luzl 写道
我一直在想一个问题,做循环的时候变量在循环体内定义省内存还是变量在循环体外定义省内存,于是做了下实验:
	public void testname() throws Exception {
		for (int i = 0; i < 50000; i++) {
			HoldMemory holdMemory=new HoldMemory();
		}
		System.out.println(Runtime.getRuntime().freeMemory());
		
	}
}


和下面的code进行比较:
	public void testname() throws Exception {
                HoldMemory holdMemory=null;
		for (int i = 0; i < 50000; i++) {
		     holdMemory=new HoldMemory();
		}
		System.out.println(Runtime.getRuntime().freeMemory());
		
	}
}


Java是编译型语言不是解释型语言。
上面两段代码生成的字节码,对于方法testname() 而言,其局部变量都是3个:this、i、holdMemory,
即使看上去“HoldMemory holdMemory”是在循环体内声明的,holdMemory变量也仍然只有一个。

这两段代码生成的字节码的唯一区别是 第一段代码前面多了局部变量的初始化操作。

第一段代码对应的字节码:


第二段代码对应的字节码:


4 楼 chcc1023 2010-05-10  
这种小测试感觉是没什么区别,但是拿到项目中应用呢?
3 楼 liwenjie 2010-05-10  
首先需要清楚内存分配有堆栈和堆,就好比String s=new String("2")有几个对象的问题,s放在堆栈上,new的对象放在堆上,在循环内定义变量,变量在堆栈上出了作用域就被销毁了,而定义在外,变量出了循环没有被销毁

相关推荐

    黑马程序员 - Java基础教学 - 03 - 变量的作用域、for循环、break、continue、内存结构

    黑马程序员 - Java基础教学 - 03 - 变量的作用域、for循环、break、continue、内存结构

    Java中堆内存与栈内存分配浅析

    在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配,当在一段代码块定义一个变量时,Java 就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java 会自动释放掉为该变量分配的内存...

    java内存分配详解

    栈内存中分配,当在一段代码块定义一个变量时,Java 就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java 会自动 释放掉为该变量分配的内存空间,该内存空间可以立即被另作它用。 堆内存用来存放由 new ...

    Java栈内存与堆内存

    Java把内存划分成两种:一种是栈内存,一种...当在一段代码块定义一个变量时,Java就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。

    java堆栈详解.pdf

    Java 把内存分成两种,一种叫做栈内存,一种叫做堆内存 在函数中定义的 一些基本类型...为这个变量分配内存空间,当超过变量的作用域后,java 会自动释放 掉为该变量分配的内存空间,该内存空间可以立刻被另作他用。

    Java中变量内存分配机制

     在任何编程语言中,无论是基本类型还是引用类型,不论其作用域如何,都必须为其分配一定的内存空间,Java 语言也不例外,Java 的数据类型可以分为两种:基本类型(变量持有数据本身的值)和引用类型(是某个对象...

    javascript 的变量、作用域和内存问题

    javascript 的变量、作用域和内存问题 (一)JavaScript变量可以用来保存两种类型的值:基本类型值和引用类型值。基本类型的值源自以下5种基本数据类型:Undefined、Null、Boolean、Number和 String。基本类型值和...

    Java中堆和栈的区别

    String中==与equals区别验证!...当在一段代码块定义一个变量时,Java就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。

    Java堆内存和栈内存

    当在一段代码块中定义一个变量时,java在栈中为这个变量分配内存空间,当超过变量的作用域后,java会自动释放掉为该变量分配的内存空间,该内存空间可以立刻被另作他用。  堆内存用于存放由new创建的对象和数组。...

    简单谈谈javascript中的变量、作用域和内存问题

    【变量】 [1]定义:可变的量,相当于给一个不定的数据起了一个外号。变量是存储信息的容器。 [2]特性:js中的变量是松散类型的,可以...[4]注意:用var操作符定义的变量将成为定义该变量的作用域中的局部变量。若省

    Java岗面试核心MCA版

    java核心面试题汇总,包含问题和答案!! 普通类和抽象类有哪些区别? 普通类不能包含抽象方法,抽象类可以包含抽象...作用域 成员变量:针对整个类有效。 局部变量:只在某个范围内有效。(一般指的就是方法,语句体内)

    Android通过全局变量传递数据

    在Activity之间数据传递中还有一种比较实用的方式 就是全局对象 实用J2EE的读者来说都知道Java Web的四个作用域 这四个作用域从小到大分别是Page Request Session和Application 其中Application域在应用程序的任何...

    关于javascript作用域的常见面试题分享

    每一个变量、函数都有其作用的范围,超出作用不得使用,这个叫做作用域。 二、全局变量、局部变量: 1.全局变量:  (1)在全局范围内声明的变量,如var a=1;  (2)只有赋值没有声明的值,如a=2; (注:如果a=2...

    JAVA内存结构详解

     (4)当在一段代码块中定义一个变量时,java在栈中为这个变量分配内存空间,当超过变量的作用域后,java会自动释放掉为该变量分配的内存空间,该内存空间可以立刻被另作他用。  (5)当数据使用完,所占空间会

    125集专攻JAVA基础 JAVA零基础入门学习视频教程 动力节点JAVA视频教程.txt

    北京动力节点-Java编程零基础教程-116-Java基本语法-方法详解-变量的作用域.avi 北京动力节点-Java编程零基础教程-117-Java基本语法-方法详解-方法的调用过程-栈结构及虚拟机栈.avi 北京动力节点-Java编程零基础...

    Java提高篇之关键字static

    一、 static代表着什么  在Java中并不存在全局变量的概念,但是我们可以通过static来实现一个“伪全局”的概念,在Java中static表示“全局”或者“静态”的意思,...同时如果静态的成员变量和成员方法不出作用域的

    [java]读书笔记整理:一切都是对象

    所以,在C和C++里将一个较大作用域的变量“隐藏”起来的做法,在java里是不允许的。因为java设计者认为这样做会导致程序混乱。 对象的作用域 Java对象不具备和基本类型一样的生命周期。当用new创建一个java...

    js变量以及其作用域详解

    一、变量的类型 Javascript和Java、C这些语言不同,它是一种无类型、弱检测的语言。它对变量的定义并不需要声明变量类型,我们只要通过赋值的形式,可以将各种类型的数据赋值给同一个变量。例如: 代码如下: i=100;/...

    JavaSE基础学习笔记

    变量作用域 常量final 变量的命名规范 运算符 for循环 增强for循环 break continue 练习 Java的方法 1、何谓方法 2、方法的定义 方法调用 练习:比大小 面试题: 3、方法的重载 4、命令行传参 5、可变参数 6、递归 ...

    Java入门教程(微学苑)-part1

    3.12 Java变量的作用域 48 3.13 Java this关键字详解 49 3.14 使用this区分同名变量 49 3.15 作为方法名来初始化对象 50 3.16 作为参数传递 51 3.17 Java方法重载 52 3.18 Java类的基本运行顺序 53 3.19 Java包装类...

Global site tag (gtag.js) - Google Analytics