草庐IT

java - 堆栈上无法访问的对象不能被垃圾回收

coder 2024-03-06 原文

出乎我的意料,下面的程序

import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.List;

public class StackTest {
  public static void main(String[] args) {
    Object object1 = new Object();
    Object object2 = new Object();
    List<Object> objects = Arrays.asList(object1, object2);

    WeakReference<Object> ref1 = new WeakReference<>(object1);
    WeakReference<Object> ref2 = new WeakReference<>(object2);

    for (Object o : objects) {
      System.out.println(o);
    }
    objects = null;

    object1 = null;
    object2 = null;

    System.gc();
    System.gc();
    System.gc();

    System.out.println("ref1: " + ref1.get());
    System.out.println("ref2: " + ref2.get());
  }
}

仍然打印出来

ref1: java.lang.Object@15db9742
ref2: java.lang.Object@6d06d69c

意思是 object1object2 不是 GC-ed。

但是,当从程序中删除 for 循环时,这些对象可以被 GC 处理并打印程序。

ref1: null
ref2: null

for 循环移动到一个单独的方法具有相同的效果:对象在程序结束时被 GC-ed。

我怀疑正在发生的事情是 for 循环将这些对象存储在堆栈中,并且之后不会删除它们。由于该对象仍存在于堆栈中,因此无法对其进行 GC。

看字节码(我确实不太擅长)似乎支持这个假设:

53: invokeinterface #6,  1            // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
58: astore        6
60: aload         6
62: invokeinterface #7,  1            // InterfaceMethod java/util/Iterator.hasNext:()Z
67: ifeq          90
70: aload         6
72: invokeinterface #8,  1            // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
77: astore        7
79: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
82: aload         7
84: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
87: goto          60

我看到了 astore 命令,但我无法在字节码中找到再次从堆栈中删除这些命令的位置。

但是,我的理论有两个问题:

  • 根据我对该字节码的理解,我预计 object1 已从堆栈中移除(被 object2 覆盖),并且只有最后一个访问的对象循环 (object2) 不会被 GC-ed。
  • for 循环更改为

    for (Object o : objects) {
      System.out.println(o);
      o = null;
    }
    

    不会改变程序的输出。我原以为这会清除对堆栈中对象的引用。

问题:有人对为什么 for 循环确保这些对象不能被 GC 有可靠的理论吗?我的理论中存在一些漏洞。

上下文: 我们在基于 Netbeans 方法检测内存泄漏的单元测试中遇到了这个问题 NBTestCase#assertGC .当对象仍在堆或堆栈上被引用时,此 assertGC 方法将失败。

在我们的测试中,我们有这样的代码

@Test
public void test(){
  List<DisposableFoo> foos = ...;

  doStuffWithFoo(foos);

  List<WeakReference<DisposableFoo>> refs = ...;

  for(DisposableFoo foo : foos){
    disposeFoo(foo);
  }

  foos = null;
  assertGC(refs);
}

一直失败,直到我们删除了 for 循环。

我们已经有了解决方法(将 for 循环移至单独的方法),但我想了解为什么我们的原始代码不起作用。

最佳答案

问题是您在堆栈上仍然有一个列表迭代器,并且该列表迭代器具有对原始列表的引用。这使列表保持 Activity 状态,就像您从未设置过 objects 一样。为空。

迭代器必须保留对原始集合的引用,以便它可以请求下一个 项等。对于常规Iterator<E>它可以可能将其内部引用设置为null一次hasNext()已返回 false,但对于列表迭代器,情况并非如此,因为您可以在列表迭代器内双向移动。

关于java - 堆栈上无法访问的对象不能被垃圾回收,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45476773/

有关java - 堆栈上无法访问的对象不能被垃圾回收的更多相关文章

  1. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  2. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  3. ruby-on-rails - 由于 "wkhtmltopdf",PDFKIT 显然无法正常工作 - 2

    我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-

  4. ruby-on-rails - 按天对 Mongoid 对象进行分组 - 2

    在控制台中反复尝试之后,我想到了这种方法,可以按发生日期对类似activerecord的(Mongoid)对象进行分组。我不确定这是完成此任务的最佳方法,但它确实有效。有没有人有更好的建议,或者这是一个很好的方法?#eventsisanarrayofactiverecord-likeobjectsthatincludeatimeattributeevents.map{|event|#converteventsarrayintoanarrayofhasheswiththedayofthemonthandtheevent{:number=>event.time.day,:event=>ev

  5. ruby-on-rails - 无法使用 Rails 3.2 创建插件? - 2

    我对最新版本的Rails有疑问。我创建了一个新应用程序(railsnewMyProject),但我没有脚本/生成,只有脚本/rails,当我输入ruby./script/railsgeneratepluginmy_plugin"Couldnotfindgeneratorplugin.".你知道如何生成插件模板吗?没有这个命令可以创建插件吗?PS:我正在使用Rails3.2.1和ruby​​1.8.7[universal-darwin11.0] 最佳答案 随着Rails3.2.0的发布,插件生成器已经被移除。查看变更日志here.现在

  6. ruby-on-rails - 在混合/模块中覆盖模型的属性访问器 - 2

    我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah

  7. ruby - 无法运行 Rails 2.x 应用程序 - 2

    我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby​​:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r

  8. ruby-on-rails - 如何验证非模型(甚至非对象)字段 - 2

    我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss

  9. ruby-on-rails - 无法在centos上安装therubyracer(V8和GCC出错) - 2

    我正在尝试在我的centos服务器上安装therubyracer,但遇到了麻烦。$geminstalltherubyracerBuildingnativeextensions.Thiscouldtakeawhile...ERROR:Errorinstallingtherubyracer:ERROR:Failedtobuildgemnativeextension./usr/local/rvm/rubies/ruby-1.9.3-p125/bin/rubyextconf.rbcheckingformain()in-lpthread...yescheckingforv8.h...no***e

  10. Ruby 写入和读取对象到文件 - 2

    好的,所以我的目标是轻松地将一些数据保存到磁盘以备后用。您如何简单地写入然后读取一个对象?所以如果我有一个简单的类classCattr_accessor:a,:bdefinitialize(a,b)@a,@b=a,bendend所以如果我从中非常快地制作一个objobj=C.new("foo","bar")#justgaveitsomerandomvalues然后我可以把它变成一个kindaidstring=obj.to_s#whichreturns""我终于可以将此字符串打印到文件或其他内容中。我的问题是,我该如何再次将这个id变回一个对象?我知道我可以自己挑选信息并制作一个接受该信

随机推荐