清除或设置null到java中的对象
我最近正在考虑释放Java对象占用的内存。 在这样做时,我对如何在Java中复制(浅/深)对象以及如何避免在对象仍处于使用状态时意外清除/无效而感到困惑。
请考虑以下方案:
- 将
ArrayList
作为参数传递给方法。 - 将
ArrayList
传递给可由线程处理的可运行类。 - 将
ArrayList
放入HashMap
。
现在在这种情况下,如果我调用list = null;
或list.clear();
,对象会发生什么? 在这种情况下,对象会丢失,在哪种情况下只将引用设置为null?
我想这与浅层和深层复制对象有关,但在哪些情况下会发生浅层复制,在这种情况下,深层复制是否发生在Java中?
首先,您永远不会将对象设置为null。 这个概念毫无意义。 您可以为变量赋值null
,但需要非常仔细地区分“变量”和“对象”的概念。 一旦你这样做,你的问题就会回答:)
现在在“浅拷贝”和“深拷贝”方面 – 这里可能值得避免使用术语“浅拷贝”,因为通常浅拷贝涉及创建新对象,而只是直接复制现有对象的字段。 深拷贝将获取这些字段引用的对象的副本(对于引用类型字段)。 这样一个简单的任务:
ArrayList list1 = new ArrayList (); ArrayList list2 = list1;
……在这个意义上,不做浅拷贝或深拷贝。 它只是复制参考。 在上面的代码之后, list1
和list2
是独立变量 – 它们恰好恰好具有相同的值(引用)。 我们可以改变其中一个的值,它不会影响另一个:
list1 = null; System.out.println(list2.size()); // Just prints 0
现在,如果不是更改变量 ,而是对变量值引用的对象进行更改,那么更改也将通过另一个变量可见:
list2.add("Foo"); System.out.println(list1.get(0)); // Prints Foo
回到原来的问题 – 你永远不会将实际对象存储在地图,列表,数组等中。您只能存储引用 。 当没有“实时”代码到达该对象的方式时,对象只能被垃圾收集。 所以在这种情况下:
List list = new ArrayList (); Map> map = new HashMap>(); map.put("Foo", list); list = null;
… ArrayList
对象仍然无法进行垃圾回收,因为Map
有一个引用它的条目。
清除变量
据我所知,
如果要重用该变量,请使用
Object.clear();
如果您不打算重用,那么定义
Object=null;
注意:与removeAll()相比,clear()更快。
请纠正我,如果我错了……
这取决于有多少变量引用到每个对象,为了解释这一点,一些代码会更好:
Object myAwesomeObject = new Object(); List
因此,它不依赖于您是否将ArrayList作为参数传递给方法等,它取决于仍然有多少变量引用您的对象。
如果将列表放入哈希映射中,则哈希映射现在包含对列表的引用。
如果将列表作为参数传递给方法,则该方法将在方法持续时间内对其进行引用。
如果将其传递给要操作的线程,则该线程将具有对该对象的引用,直到它终止。
在所有这些情况下,如果设置list = null
,仍会保留引用,但在这些引用消失后它们将消失。
如果你只是清除列表,引用仍然有效,但现在将指向一个突然被清空的列表,程序员可能不知道这可能被认为是一个错误,特别是如果你使用该线程。
如果您将一个ArrayList传递给一个方法,那么如果在某个地方存在对该列表的实时引用,例如在调用代码中,则list = null将不起作用。 如果在代码中的任何位置调用list.clear(),则对此列表中对象的引用将为空。 传递方法的引用并不浅,它传递引用的按值
Java GC在任何地方未被引用时自动声明对象。 因此,在大多数情况下,您必须明确地将引用设置为null
一旦变量的范围结束,对象就有资格获得GC,并且如果没有其他引用指向该对象,则会释放该对象。
Java是按值传递的,因此如果在方法中将列表设置为null
,则它不会影响在方法中传递给您的原始引用。
public class A{ private List list = new ArrayList (); public static void main(String[] args) { A a = new A(); B b = new B(); b.method(a.list); System.out.println(a.list.size()); //Will print 0 and not throw NullPointerException } } class B{ public void method(List list){ list = null; //just this reference is set to null and not the original one //so list of A will not be GCed } }
我最近正在考虑释放java对象占用的内存。
一个忠告。
考虑这一点通常是一个坏主意 。 尝试“帮助”通常是一个更糟糕的主意。 在99.8%的情况下,Java垃圾收集器能够更好地收集垃圾,如果你实际上只是让它继续使用它……并且不要浪费你的努力来为事物分配null
。 实际上,您正在归零的字段很可能是无论如何都要无法访问的对象。 在这种情况下,GC甚至不会查看您已经填充的字段。
如果你采用这种(实用的)观点,那么你对浅层复制品和深层复制品的所有想法,以及何时可以安全地废弃物品都是没有意义的。
在一小部分情况下,建议指定null
…以避免中期或长期存储泄漏。 如果您处于“回收”对象的极少数情况之一实际上是个好主意,那么建议使用归零。