朋友,您在使用Java进行编程时,是否了解过其调用内存的工作原理?总的说来,作为一个不错的静默式垃圾回收器,Java具有自动管理内存的功能,能够在后台工作,清理未使用的对象,并释放内存。话虽如此,如果您的程序设计不到位,Java的垃圾收集器和内存管理特性,恐怕也无法自动生效。
网站建设哪家好,找成都创新互联!专注于网页设计、网站建设、微信开发、小程序设计、集团企业网站建设等服务项目。为回馈新老客户创新互联还提供了通化免费建站欢迎大家使用!
可见,了解内存在Java中的实际原理是至关重要的。它不但能够辅助您编写出高性能的应用程序,还能够尽量避免程序因OutOfMemoryError而崩溃;或者是在程序运行状况不佳时,协助您快速发现内存泄漏的原因。
下面,让我们首先来看一下Java语言中的内存组织结构:
如上图所示,内存通常被分为两大部分:栈和堆。请记住,该图片中的内存类型大小与实际内存大小并不成比例。也就是说:与栈相比,堆是更大块的内存。
栈内存既负责保存那些针对堆对象(heap objects)的引用,又负责保存各种值的类型,即:存储的是数值本身,而不是对堆中某个对象的引用。在Java中,我们称为原始类型(primitive types)。
另外,栈上的变量具有一定的可见性,我们称为范围(scope)。通常,只有活跃范围(active scope)中的对象,才可以被使用。例如:假设我们没有任何全局作用域的变量(或字段),而只有局部的变量,那么如果编译器要执行某个方法的主体,就只能从栈中访问该方法主体内的对象。而且由于超出了范围,因此它无法访问其他局部变量。一旦该方法被执行完成并给出了返回,它就会弹出栈的顶部,并更改活跃范围。
也许您已经注意到,由于Java的栈内存是按照线程分配的,因此在上图中会有多个栈存储器。而且,程序在每次创建和启动一个线程时,都拥有自己的栈内存,无需也无法访问另一个线程的栈内存。
这部分的内存存储着实际对象,它们会被栈的变量所引用。让我们来看如下代码行:
- StringBuilder builder = new StringBuilder();
关键字new负责确保堆能够获取足够的可用空间。它在存储器中创建StringBuilder类型的对象,并通过“builder”的引用,其压入栈中。
由于每个正在运行的JVM进程只有一个堆内存,因此无论系统当前正在运行多少个线程,它们都会共享内存的指定部分。实际上,堆的真实结构与上图不尽相同,它会根据垃圾收集的过程,被分成几个部分。
而是否需要预定义栈和堆的最大容量,将完全取决于正在运行程序的计算机。在后面的讨论中,我们将研究JVM的相关配置,以便为正在运行的应用程序,显式地指定大小。
如果仔细观察上述图片,您可能会注意到,来自于堆的、表示对象引用的箭头,实际上具有不同的类型。这是因为在Java编程语言中,我们具有不同类型的引用,即:强引用、弱引用、软引用、以及虚引用(phantom references)。引用类型之间的区别在于:堆上的对象在不同条件下,可以引用的垃圾回收有所不同。下面,我们来逐个进行讨论。
1.强引用
这是最流行,也是开发人员最常用的引用类型。在上述StringBuilder示例中,我们实际上对堆中的对象采取了强引用。堆上的对象不会被垃圾回收,而是有一个指向了它的强引用,或者通过一串强引用来获取该对象。
2.弱引用
弱引用可通过如下方式被创建:
- WeakReference reference = new WeakReference<>(new StringBuilder());
弱引用的一种最佳使用场景是缓存方案。设想,您检索了一些数据,并且希望将其存储在内存中,以便下次能够直接作出响应。当然,您并不确定何时或者是否有对该数据的请求。那么,您就可以对其采用弱引用,以免堆上的对象被垃圾收集器回收掉,以致在检索该对象时,返回null值。可见,WeakHashMap
- /**
- * The entries in this hash table extend WeakReference, using its main ref
- * field as the key.
- */
- private static class Entry
extends WeakReference