JVM优化——一些基本概念(01)

JDK、JRE和JVM是Java编程语言的核心概念,尽管它们看起来差不多,作为程序员我们可能不怎么关心,但是作为专业的程序员我们必须了解清楚。

1. JDK、JRE、JVM概念

JDK(Java开发工具包)

JDK是Java的核心组件,并提供编译、调试和运行一个Java程序所需要的所有工具,可执行文件和二进制文件,JDK针对不同的平台有不同的安装包,可以说JDK是JRE的超集,它包含了JRE的Java编译器,调试器和核心类。

JRE(Java运行环境)

JRE提供了运行Java程序的平台,JRE包含了JVM、Java二进制文件和其他成功执行程序的类文件,如果仅仅是执行Java程序,只需要安装JRE即可,不需要JDK。

JVM(Java虚拟机)

JVM是Java编程语言的核心,当运行一个程序时,JVM负责将字节码转换成特定的机器代码,JVM是平台特定的,并提供核心的Java方法,如内存管理、垃圾回收和安全机制等。JVM之所以被称为虚拟的是因为它提供了一个不依赖于底层操作系统和机器硬件的接口,这种独立于硬件和操作系统的特性正是Java程序可以一次编译多处执行的原因。

JDK、JRE和JVM的区别
JDK是用于开发的而JRE是用于运行Java程序的
JDK和JRE都包含了JVM,从而使得我们可以运行Java程序
JVM是Java编程语言的核心并且具有平台独立性

2. 堆和栈

堆和栈是程序运行时的关键,栈解决程序运行时的问题,即程序如何执行或者如何处理数据,堆解决的是数据存储的问题,即数据怎么放,放在哪儿。Java中一个线程和一个线程栈相对应,里面存储的都是当前线程相关的信息,包括局部变量、运行状态、方法返回值等。而堆是所有线程共享的只负责存储对象信息。

堆中存储的是对象,栈中存储的是基本数据类型和堆对象的引用,一个对象的大小是不可估计的或者说是动态变化的,而栈中一个对象只对应了4byte的引用。栈中存储的都是基本数据类型一般是1~8个字节,需要空间较少,也不会出现动态增长的情况,因此使用栈存储就已经足够了。栈是程序运行最根本的东西,程序运行可以没有堆,但是不能没有栈,否则会出现java.lang.StackOverflowError异常。

那么Java中参数传递时是传递引用还是传值?首先明确一点,程序运行永远都是在栈中执行的,因而参数传递时,只存在传递基本类和对象引用的问题,不会直接传递对象本身。基本类型和引用的处理是一样的,都是传值,所以如果是传引用的方法调用,也同时理解为“传引用值”的传值调用,即引用的处理跟基本类型是完全一致的,但是当进入被调用方法时,被传递的引用被程序解释到堆中的对象,这个时候才是对应到真正的对象,如果此时进行修改,修改的是引用对应的对象,而不是引用本身,即,修改的是堆中的数据。

Java对象的大小

基本数据类型的大小是固定的,而对应非基本数据类型的对象,其大小就值得商榷。在Java中一个空的Object对象的大小是8byte,这个大小只是保存堆中一个没有任何属性的对象的大小,如下面语句:

1
Object obj = new Object();

这样就在程序中生成了一个Object对象,其所占用空间为:4byte+8byte,4byte是指java栈中保存引用的所需要的空间,因为所以Java非基本类型的对象都需要默认继承Object对象,其大小都必须大于8byte,有了Object对象的大小那么就可以计算其他对象的大小了,如:

1
2
3
4
5
Class NewObject{
int count;
boolean flag;
Object obj;
}

其大小为:空对象大小(8byte) + int大小(4byte) + boolean大小(1byte) + 空Object引用大小(4byte) = 17byte 但是Java在对对象分配时都是以8的整数倍来分,因此此对象的大小为24byte。这里要注意一下基本类型的包装类型的大小,因为这种包装类已经成为对象了,所以其大小至少是12byte,又因为Java对象大小是8的整数倍,因此一个基本类型的包装类至少是16byte,这个内存占用是很恐怖的,因此可能的话尽量少使用包装类。

3. 引用类型

引用类型分为强引用、软引用、弱引用和虚引用

强引用

强引用是使用最普遍的引用,如果一个对象具有强引用,那垃圾回收器绝不会回收它,当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会随意回收具有强引用的对象来解决内存不足的问题。

软引用(SoftReference)

如果一个对象具有软引用,如果内存空间足够,垃圾回收就不会回收它,如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用,软引用可以用来实现内存敏感的高速缓存。软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。

弱引用(WeakReference)

如果一个对象只具有弱引用,那就类似于可有可物的生活用品。弱引用与软引用的区别在于:只具有弱引用的对象拥有更 短暂的生命周期。在垃圾回收器线程扫描它 所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程, 因此不一定会很快发现那些只具有弱引用的对象。 弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。

虚引用(PhantomReference)

“虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。虚引用主要用来跟踪对象被垃圾回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

在java.lang.ref包中提供了三个类:SoftReference类、WeakReference类和PhantomReference类,它们分别代表软引用、弱引用和虚引用。ReferenceQueue类表示引用队列,它可以和这三种引用类联合使用,以便跟踪Java虚拟机回收所引用的对象的活动。

参考资料
码农的士: [http://www.dexcoder.com/selfly/article/313]
CSDN:[http://blog.csdn.net/coding_or_coded/article/details/6603549]
CSDN:[http://blog.csdn.net/u011936381/article/details/11709245]
ImportNew:[http://blog.csdn.net/coding_or_coded/article/details/6603549]

坚持原创技术分享,您的支持将鼓励我的继续创作