龙空技术网

七爪源码:Java中不同类型的引用以及如何使用它们

庄志炎 424

前言:

现在看官们对“java调用其他程序”大致比较着重,小伙伴们都想要知道一些“java调用其他程序”的相关知识。那么小编同时在网络上网罗了一些对于“java调用其他程序””的相关文章,希望你们能喜欢,小伙伴们一起来学习一下吧!

有些人惊讶地发现语言中有几种类型的参考。 通常它们被用于某些内部或框架的实现,因此大多数人不会直接接触它们。 它们也没有在大多数 Java 应用程序中使用,因为它们带来了额外的复杂性,并且需要更深入地了解 GC 的工作原理。 然而,这并不意味着它们不应该被使用,事实上在某些用例中它们是最好的选择,并且可以成为解决复杂问题的优雅解决方案。 它们使我们能够充分利用 GC 及其复杂的内存管理算法,并就哪些对象应该活得更久以及应该立即处理/收集哪些对象提供高级提示。

Java 语言中有四种类型的引用

强参考

这是每个人在日常工作中使用的正常参考。 通过强引用可访问的对象不符合收集条件。 当我们将 null 分配给引用或者它超出范围并从堆栈中删除时,GC 会将对象标记为符合收集条件。

让我们看一些代码

void iAmJustAMethod() {  Object object = new Object();  object = null;          object = new Object();}

在第 2 行发生了一些事情。首先创建一个名为 object 的强引用和一个新对象。然后'='操作符指向对象的引用。引用位于堆栈中,对象位于堆中。

在第 3 行,我们断开连接。从现在开始,该对象被视为符合收集条件。但是,直到下一个 GC 周期才会收集它。

现在第 5 行做了两件事。创建一个全新的对象,然后将引用指向它。在第 6 行,该方法退出。当一个方法退出时,所有存在于其范围内的变量都会以相反的顺序被删除(因此是名称堆栈)。这意味着此时对象引用将不复存在,并且该对象将被标记为符合收集条件(并最终被收集)。

我的猜测是你们中的大多数人已经熟悉这种类型的参考以及它是如何工作的(这是最后一个非常常见的面试问题 :D)我只是想把你的注意力引向对象 GC 资格,所以我们可以转到其他引用类型。

弱参考

WeakReference 类为我们提供了弱引用。与强引用不同,它们不会阻止 GC 收集对象。

Object strongReference = new Object();WeakReference<Object> weakRef = new WeakReference<>(strongReference);strongReference = null;long start = System.currentTimeMillis();while (weakRef.get() != null) {  // weakRef still hold the object  System.out.println(weakRef.get());}long end = System.currentTimeMillis() - start;System.out.println(end);System.out.println(weakRef);

第 13 行和第 14 行的日志为我们提供以下信息

1066java.lang.ref.WeakReference@251a69d7

那我们来分析一下代码。 首先我们定义一个对象的强引用,然后在第 2 行我们指向它的弱引用。 然后我们将强引用设置为null,这将使对象有资格被收集,我们只有指向它的弱引用。 然后我们循环直到弱引用中的对象变为空,这意味着它被收集了。 在我的机器上它存在大约 1 秒,因为这是我的 JVM 开始的 GC 间隔(这可能会根据运行时内存使用情况而改变超时)。 本质上,我们持有该对象直到下一次 GC。

WeakHashMap

Map<Object, Object> weakMap = new WeakHashMap<>();Object key = new Object();weakMap.put(key, new HugeObject());key = null;System.gc();while (true) {  System.out.println(weakMap);}

这可能是弱引用最常见的用例。 WeakHashMap 是一种特殊的映射,它永远不会为键创建强引用。这意味着单独的地图不会阻止其值被收集,使其成为短期缓存的理想选择(如上面的代码所示)。我们依靠 GC 为我们收集对象,我们不需要为此烦恼。本质上,如果某些其他代码使用映射中的值(通过其键),它将持有对它的强引用,从而使该值不符合 GC 条件。这使我们可以完全根据使用情况创建动态缓存。你必须承认这很整洁。

弱引用的其他用例

您希望保留对某事物的引用,直到存在对它的强引用。例如,将侦听器定义为弱引用将继续存在,直到它有指向它的对等方。如果这是一个强参考,您必须手动停止侦听器它们可用于防止内存泄漏。网上很多人都举了这样的例子,但我找不到一个好的。在我看来,内存泄漏通常是由错误的代码引起的,使用弱引用来解决它并不能解决只是隐藏它的问题。

软引用

软引用的行为与弱引用非常相似,不同之处在于它们并不急于让对象被收集。在内存耗尽之前,它们的行为类似于强引用。如果 JVM 开始达到其内存上限,它将收集软引用对象,因此理论上它们永远不会导致 OutOfMemory 错误。让我们尝试用代码来说明

List<Object> lst = new ArrayList<>();while (true) {  lst.add(Files.readAllBytes(Paths.get("huge.txt")));}

这个简单的片段导致 OOE 非常快,我们将强引用存储在列表中的大字节数组中。 这就是内存消耗的样子

我们得到一个很大的峰值,当使用的堆超过堆大小时,应用程序就会崩溃。

如果我们开始在列表中存储软引用,如下所示:

List<SoftReference<Object>> lst = new ArrayList<>();while (true) {  lst.add(new SoftReference<>(Files.readAllBytes(Paths.get("huge.txt"))));}

应用程序突然开始无限期地运行。 这是内存使用情况的样子

这次GC非常激进。您可以看到,每次使用的堆达到最大 4GB 大小时,收集器都会以软引用的形式释放千兆字节的内存。如果我们在进程运行时开始检查列表,我们会注意到它充满了一堆指向 null 的 SoftReference。当某些东西只能通过软引用访问时,我们说这个对象对应用程序来说不是必需的,如果内存开始用完,删除它是安全的。强引用被认为是必不可少的,如果 JVM 无法再支持它们,它会抛出错误,而不是继续在可能导致数据不一致的不稳定状态下运行。

您可以像使用弱引用一样使用软引用。它们可以根据用例互换。在大多数情况下,如果您的服务没有承受重负载,软引用将表现得像强引用。

幻影参考

存在幻影引用,因此我们可以跟踪 GC 何时收集对象。它们不持有对对象的任何引用(因此得名)并且不干扰其生命周期(无论我们使用哪种引用来访问它)。这是 JVM 的新终结机制,之前的终结机制(终结方法)现已弃用,不应使用。

这就是我们可以使用它们的方式

Object object = new Object();ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();PhantomReference<Object> phantomReference = new PhantomReference<>(object, referenceQueue);object = null;System.gc();System.out.println(phantomReference.get()); // always null        Reference<?> referenceFromQueue;while ((referenceFromQueue = referenceQueue.poll()) != null) {  // This line will be reached only when the object is collected  System.out.println(referenceFromQueue);}

非常类似地,我们创建一个对象,然后将 PhantomReference 指向它。 但是,我们还必须提供一个特殊的 ReferenceQueue。 在第 6 行,我们向 JVM 建议 GC(并希望它能够满足我们的要求)。 pahntomReference.get() 总是给我们 null (我们没有到对象的链接,只有它的生命周期)。 第 12 行的代码将等待 GC 收集对象。 如果你有一个特殊的终结逻辑,或者你想在创建下一个之前等待一个巨大的对象被收集,这将非常有用。

幻影参考的用途非常有限,通常您不会经常看到它们。 然而,在微调某些具有大量堆的应用程序的性能时,它们可以证明是至关重要的。

关注七爪网,获取更多APP/小程序/网站源码资源!

标签: #java调用其他程序