堆和栈
栈与堆都是Java用来在内存中存放数据的地方。
A.堆--用new建立,垃圾自动回收负责回收
1、堆是一个"运行时"数据区,类实例化的对象就是从堆上去分配空间的;
2、在堆上分配空间是通过"new"等指令建立的;
3、Java针对堆的操作和C++的区别就是,Java不需要在空间不用的时候来显式的释放;
4、Java的堆是由Java的垃圾回收机制来负责处理的,堆是动态分配内存大小,垃圾收集器可以自动回收不再使用的内存空间。
5、但缺点是,因为在运行时动态分配内存,所以内存的存取速度较慢。
例:String str = new String("abc");
B.栈--存放基本数据类型,速度快
1、栈中主要存放一些基本类型的变量(int, short, long, byte, float, double, boolean,
char)和对象句柄;
2、栈的存取速度比堆要快;
3、栈数据可以共享;
4、栈的数据大小与生存期必须是确定的,缺乏灵活性。
例:int a = 3;
C.何谓栈的"数据共享
什么是栈的"数据共享"?
这里面所说的数据共享,并不是由程序员来控制的,而是JVM来控制的,指的是是系统自动处理的方式。
比如定义两个变量:
1 int a = 5;
2 int b = 5;
这两个变量所指向的栈上的空间地址是同一个,这就是所谓的"数据共享"。
它的工作方式是这样的:
JVM处理int a = 5,首先在栈上创建一个变量为a的引用,然后去查找栈上是否还有5这个值,如果没有找到,那么就将5存放进来,然后将a指向5。接着处理int b = 5,在创建完b的引用后,因为在栈中已经有5这个值,便将b直接指向5。于是,就出现了a与b同时指向5的内存地址的情况。
D.实例化对象的两种方法
对于String这个类来说它可以用两种方法进行建立:
String s = new String("asdf");
|
和
用这两个形式创建的对象是不同的,第一种是用new()来创建对象的,它是在堆上开辟空间,每调用一次都会在堆上创建一个新的对象。
而第二种的创建方法则是先在栈上创建一个String类的对象引用,然后再去查找栈中有没有存放"asdf",如果没有,则将"asdf"存放进栈,并让str指向"asdf",如果已经有"asdf"
则直接把str指向"abc"。
我们在比较两个String是否相等时,一定是用"equals()"方法,而当测试两个包装类的引用是否指向同一个对象时,我们应该用"= ="。
因此,我们可以通过"= ="判断是否相等来验证栈上面的数据共享的问题。
例1:
1 String s1 = "asdf";
2 String s2 = "asdf";
3 System.out.println(s1==s2);
该程序的运行结果是,"true",那么这说明"s1"和"s2"都是指向同一个对象的。
例2:
1 String s1 =new String ("asdf");
2 String s2 =new String ("asdf");
3 System.out.println(s1==s2);
该程序的运行结果是,"false",这说明用new的方式是生成的对象,每个对象都指向不同的地方。
两个读取内存信息函数
A.Java给我们提供了读取内存信息的函数,这两个函数分别是:
1、Runtime.getRuntime().maxMemory()
得到虚拟机可以控制的最大内存数量。
2、Runtime.getRuntime().totalMemory()
得到虚拟机当前已经使用的内存数量。
B.开发Java程序内存看的见
为了看看这两个函数的运行效果,我们可以编一个小程序来看看这两个程序的实际运行效果,让大家也对内存的占用情况做到"看得见摸得着"。
1 public class MemoryTest{
2 public static void main(String args[]){
3 String s="abcdefghijklmnop";
4 System.out.print("maxMemory:");
5 System.out.println(Runtime.getRuntime().maxMemory()/1024/1024+"M");
6 System.out.print("totalMemory:");
7 System.out.println(Runtime.getRuntime().totalMemory()/1024/1024+"M");
8 for(int i=0;i<19;i++){
9 s=s+s;
10 }
11 System.out.print("totalMemory:");
12 System.out.println(Runtime.getRuntime().totalMemory()/1024/1024+"M");
13 }
14 }
这个程序运行效果如下图
3 1所示。
从这个程序的输出结果可以发现两个值得注意的地方:
1、竟然虚拟机可以控制的最大内存数量只有63M,也就是说,稍微不留神就会发生内存溢出。
2、在不断的向String中追加字符的情况下,所占用的内存在不断的增长。
不是开玩笑,Java虚拟机在默认的情况下只能控制"66650112
byte"即"63.56M"。那么,我们如何才能让Java虚拟机能够控制更多的内存呢?
4.3.3 必须要介绍的虚拟机的参数"-Xmx"
Java程序员往往似乎已经习惯了使用:
java -classpath program [回车]
|
来启动Java应用程序,这是用于一般程序的启动方式。其实JVM其中的相关参数还是有很多的,而这些参数对于应用程序的运行是非常有用的。
我们可以进入Windows的cmd控制台窗口,键入如下指令:
在屏幕上可以看到如下内容:
Usage: java [-options] class [args...]
(to execute a class)
or java [-options] -jar jarfile [args...]
(to execute a jar file)
where options include:
-client to select the "client" VM
-server to select the "server" VM
-hotspot is a synonym for the "client" VM [deprecated]
The default VM is client.
-cp <class search path of directories and zip/jar files>
-classpath <class search path of directories and zip/jar files>
A ; separated list of directories, JAR archives,
and ZIP archives to search for class files.
-D<name>=<value>
set a system property
-verbose[:class|gc|jni]
enable verbose output
-version print product version and exit
-version:<value>
require the specified version to run
-showversion print product version and continue
-jre-restrict-search | -jre-no-restrict-search
include/exclude user private JREs in the version search
-? -help print this help message
-X print help on non-standard options
-ea[:<packagename>...|:<classname>]
-enableassertions[:<packagename>...|:<classname>]
enable assertions
-da[:<packagename>...|:<classname>]
-disableassertions[:<packagename>...|:<classname>]
disable assertions
-esa | -enablesystemassertions
enable system assertions
-dsa | -disablesystemassertions
disable system assertions
-agentlib:<libname>[=<options>]
load native agent library <libname>, e.g. -agentlib:hprof
see also, -agentlib:jdwp=help and -agentlib:hprof=help
-agentpath:<pathname>[=<options>]
load native agent library by full pathname
-javaagent:<jarpath>[=<options>]
load Java programming language agent, see java.lang.instrument
|
其他参数基本上都有相关说明,我们不在这里赘述,现在只关注其中的"-X"这参数。"-X"的说明中说:"print help on non-standard
options",意思是"打印非标准配置参数的帮助信息",也就是说,如果直接键入"java -X"的话,将会看到非标准参数的说明。
好,继续在CMD中键入:
运行后得到了如下结果:
-Xmixed mixed mode execution (default)
-Xint interpreted mode execution only
-Xbootclasspath:<directories and zip/jar files separated by ;>
set search path for bootstrap classes and resources
-Xbootclasspath/a:<directories and zip/jar files separated by ;>
append to end of bootstrap class path
-Xbootclasspath/p:<directories and zip/jar files separated by ;>
prepend in front of bootstrap class path
-Xnoclassgc disable class garbage collection
-Xincgc enable incremental garbage collection
-Xloggc:<file> log GC status to a file with time stamps
-Xbatch disable background compilation
-Xms<size> set initial Java heap size
-Xmx<size> set maximum Java heap size
-Xss<size> set java thread stack size
-Xprof output cpu profiling data
-Xfuture enable strictest checks, anticipating future default
-Xrs reduce use of OS signals by Java/VM (see documentation)
-Xcheck:jni perform additional checks for JNI functions
-Xshare:off do not attempt to use shared class data
-Xshare:auto use shared class data if possible (default)
-Xshare:on require using shared class data, otherwise fail.
The -X options are non-standard and subject to change without notice.
|
现在仍然不管其他参数只看有用的几个参数:
-Xms<size> set initial Java heap size
设置JVM初始化堆内存大小
-Xmx<size> set maximum Java heap size
设置JVM最大的堆内存大小
-Xss<size> set java thread stack size
设置JVM栈内存大小
|
从这些参数的说明中得知,我们可以通过这些非标准参数来制定JVM可以控制的堆内存的大小。只需要在Java命令后面加入一个参数"-Xmx",这个参数可以指定虚拟机可以控制的内存数量,这个函数我们在前面章节也曾经见过,在这里对其详细说明一下。
例如如下命令:
java -Xmx1024m -classpath ……
|
这个命令就代表Java虚拟机控制的内存为1G,需要注意的是"-Xmx"和"1024m"之间没有空格,另外,"1024m"也可以不用"m"而直接写字节数,但是必须写字节数,如还是1G应该写成"1024000000"和"1024m"代表同样的意思。还是上一个小节的那个程序如果我们加上"-Xmx1024m"参数以后,运行结果如图
3 2所示。
通过我们对虚拟机相关参数及测试方法的了解,使我们做到了Java内存控制明明白白、心中有数。心中有数了,那就想办法让程序的内存使用效率更高一些吧。