- 浏览: 52292 次
文章分类
最新评论
-
lenozhi:
catmimigo 写道DefaultFtplet里不有很多时 ...
扩展 COMMON-NET和APACHE FTP SERVER命令 -
catmimigo:
DefaultFtplet里不有很多时间通知吗?
publi ...
扩展 COMMON-NET和APACHE FTP SERVER命令 -
lenozhi:
wangrui 写道客户端程序上传文件后直接发送RNTO(重命 ...
扩展 COMMON-NET和APACHE FTP SERVER命令 -
wangrui:
客户端程序上传文件后直接发送RNTO(重命名命令)
扩展 COMMON-NET和APACHE FTP SERVER命令 -
lenozhi:
iamlotus 写道lenozhi 写道iamlotus 写 ...
将HashMap文件化
虽然用 Java™ 语言编写的程序在理论上是不会出现“内存泄漏”的,但是有时对象在不再作为程序的逻辑状态的一部分之后仍然不被垃圾收集。本月,负责保障应用程序健康的工程师 Brian Goetz 探讨了无意识的对象保留的常见原因,并展示了如何用弱引用堵住泄漏。
要让垃圾收集(GC)回收程序不再使用的对象,对象的逻辑 生命周期(应用程序使用它的时间)和对该对象拥有的引用的实际 生命周期必须是相同的。在大多数时候,好的软件工程技术保证这是自动实现的,不用我们对对象生命周期问题花费过多心思。但是偶尔我们会创建一个引用,它在内存中包含对象的时间比我们预期的要长得多,这种情况称为无意识的对象保留(unintentional object retention)。
全局 Map 造成的内存泄漏
无意识对象保留最常见的原因是使用 Map 将元数据与临时对象(transient object)相关联。假定一个对象具有中等生命周期,比分配它的那个方法调用的生命周期长,但是比应用程序的生命周期短,如客户机的套接字连接。需要将一些元数据与这个套接字关联,如生成连接的用户的标识。在创建 Socket 时是不知道这些信息的,并且不能将数据添加到 Socket 对象上,因为不能控制 Socket 类或者它的子类。这时,典型的方法就是在一个全局 Map 中存储这些信息,如清单 1 中的 SocketManager 类所示:
这种方法的问题是元数据的生命周期需要与套接字的生命周期挂钩,但是除非准确地知道什么时候程序不再需要这个套接字,并记住从 Map 中删除相应的映射,否则,Socket 和 User 对象将会永远留在 Map 中,远远超过响应了请求和关闭套接字的时间。这会阻止 Socket 和 User 对象被垃圾收集,即使应用程序不会再使用它们。这些对象留下来不受控制,很容易造成程序在长时间运行后内存爆满。除了最简单的情况,在几乎所有情况下找出什么时候 Socket 不再被程序使用是一件很烦人和容易出错的任务,需要人工对内存进行管理。
回页首
找出内存泄漏
程序有内存泄漏的第一个迹象通常是它抛出一个 OutOfMemoryError,或者因为频繁的垃圾收集而表现出糟糕的性能。幸运的是,垃圾收集可以提供能够用来诊断内存泄漏的大量信息。如果以 -verbose:gc 或者 -Xloggc 选项调用 JVM,那么每次 GC 运行时在控制台上或者日志文件中会打印出一个诊断信息,包括它所花费的时间、当前堆使用情况以及恢复了多少内存。记录 GC 使用情况并不具有干扰性,因此如果需要分析内存问题或者调优垃圾收集器,在生产环境中默认启用 GC 日志是值得的。
有工具可以利用 GC 日志输出并以图形方式将它显示出来,JTune 就是这样的一种工具(请参阅 参考资料)。观察 GC 之后堆大小的图,可以看到程序内存使用的趋势。对于大多数程序来说,可以将内存使用分为两部分:baseline 使用和 current load 使用。对于服务器应用程序,baseline 使用就是应用程序在没有任何负荷、但是已经准备好接受请求时的内存使用,current load 使用是在处理请求过程中使用的、但是在请求处理完成后会释放的内存。只要负荷大体上是恒定的,应用程序通常会很快达到一个稳定的内存使用水平。如果在应用程序已经完成了其初始化并且负荷没有增加的情况下,内存使用持续增加,那么程序就可能在处理前面的请求时保留了生成的对象。
清单 2 展示了一个有内存泄漏的程序。MapLeaker 在线程池中处理任务,并在一个 Map 中记录每一项任务的状态。不幸的是,在任务完成后它不会删除那一项,因此状态项和任务对象(以及它们的内部状态)会不断地积累。
图 1 显示 MapLeaker GC 之后应用程序堆大小随着时间的变化图。上升趋势是存在内存泄漏的警示信号。(在真实的应用程序中,坡度不会这么大,但是在收集了足够长时间的 GC 数据后,上升趋势通常会表现得很明显。)
图 1. 持续上升的内存使用趋势
确信有了内存泄漏后,下一步就是找出哪种对象造成了这个问题。所有内存分析器都可以生成按照对象类进行分解的堆快照。有一些很好的商业堆分析工具,但是找出内存泄漏不一定要花钱买这些工具 —— 内置的 hprof 工具也可完成这项工作。要使用 hprof 并让它跟踪内存使用,需要以 -Xrunhprof:heap=sites 选项调用 JVM。
清单 3 显示分解了应用程序内存使用的 hprof 输出的相关部分。(hprof 工具在应用程序退出时,或者用 kill -3 或在 Windows 中按 Ctrl+Break 时生成使用分解。)注意两次快照相比,Map.Entry、Task 和 int[] 对象有了显著增加。
请参阅 清单 3。
清单 4 展示了 hprof 输出的另一部分,给出了 Map.Entry 对象的分配点的调用堆栈信息。这个输出告诉我们哪些调用链生成了 Map.Entry 对象,并带有一些程序分析,找出内存泄漏来源一般来说是相当容易的。
回页首
弱引用来救援了
SocketManager 的问题是 Socket-User 映射的生命周期应当与 Socket 的生命周期相匹配,但是语言没有提供任何容易的方法实施这项规则。这使得程序不得不使用人工内存管理的老技术。幸运的是,从 JDK 1.2 开始,垃圾收集器提供了一种声明这种对象生命周期依赖性的方法,这样垃圾收集器就可以帮助我们防止这种内存泄漏 —— 利用弱引用。
弱引用是对一个对象(称为 referent)的引用的持有者。使用弱引用后,可以维持对 referent 的引用,而不会阻止它被垃圾收集。当垃圾收集器跟踪堆的时候,如果对一个对象的引用只有弱引用,那么这个 referent 就会成为垃圾收集的候选对象,就像没有任何剩余的引用一样,而且所有剩余的弱引用都被清除。(只有弱引用的对象称为弱可及(weakly reachable)。)
WeakReference 的 referent 是在构造时设置的,在没有被清除之前,可以用 get() 获取它的值。如果弱引用被清除了(不管是 referent 已经被垃圾收集了,还是有人调用了 WeakReference.clear()),get() 会返回 null。相应地,在使用其结果之前,应当总是检查 get() 是否返回一个非 null 值,因为 referent 最终总是会被垃圾收集的。
用一个普通的(强)引用拷贝一个对象引用时,限制 referent 的生命周期至少与被拷贝的引用的生命周期一样长。如果不小心,那么它可能就与程序的生命周期一样 —— 如果将一个对象放入一个全局集合中的话。另一方面,在创建对一个对象的弱引用时,完全没有扩展 referent 的生命周期,只是在对象仍然存活的时候,保持另一种到达它的方法。
弱引用对于构造弱集合最有用,如那些在应用程序的其余部分使用对象期间存储关于这些对象的元数据的集合 —— 这就是 SocketManager 类所要做的工作。因为这是弱引用最常见的用法,WeakHashMap 也被添加到 JDK 1.2 的类库中,它对键(而不是对值)使用弱引用。如果在一个普通 HashMap 中用一个对象作为键,那么这个对象在映射从 Map 中删除之前不能被回收,WeakHashMap 使您可以用一个对象作为 Map 键,同时不会阻止这个对象被垃圾收集。清单 5 给出了 WeakHashMap 的 get() 方法的一种可能实现,它展示了弱引用的使用:
调用 WeakReference.get() 时,它返回一个对 referent 的强引用(如果它仍然存活的话),因此不需要担心映射在 while 循环体中消失,因为强引用会防止它被垃圾收集。WeakHashMap 的实现展示了弱引用的一种常见用法 —— 一些内部对象扩展 WeakReference。其原因在下面一节讨论引用队列时会得到解释。
在向 WeakHashMap 中添加映射时,请记住映射可能会在以后“脱离”,因为键被垃圾收集了。在这种情况下,get() 返回 null,这使得测试 get() 的返回值是否为 null 变得比平时更重要了。
用 WeakHashMap 堵住泄漏
在 SocketManager 中防止泄漏很容易,只要用 WeakHashMap 代替 HashMap 就行了,如清单 6 所示。(如果 SocketManager 需要线程安全,那么可以用 Collections.synchronizedMap() 包装 WeakHashMap)。当映射的生命周期必须与键的生命周期联系在一起时,可以使用这种方法。不过,应当小心不滥用这种技术,大多数时候还是应当使用普通的 HashMap 作为 Map 的实现。
引用队列
WeakHashMap 用弱引用承载映射键,这使得应用程序不再使用键对象时它们可以被垃圾收集,get() 实现可以根据 WeakReference.get() 是否返回 null 来区分死的映射和活的映射。但是这只是防止 Map 的内存消耗在应用程序的生命周期中不断增加所需要做的工作的一半,还需要做一些工作以便在键对象被收集后从 Map 中删除死项。否则,Map 会充满对应于死键的项。虽然这对于应用程序是不可见的,但是它仍然会造成应用程序耗尽内存,因为即使键被收集了,Map.Entry 和值对象也不会被收集。
可以通过周期性地扫描 Map,对每一个弱引用调用 get(),并在 get() 返回 null 时删除那个映射而消除死映射。但是如果 Map 有许多活的项,那么这种方法的效率很低。如果有一种方法可以在弱引用的 referent 被垃圾收集时发出通知就好了,这就是引用队列 的作用。
引用队列是垃圾收集器向应用程序返回关于对象生命周期的信息的主要方法。弱引用有两个构造函数:一个只取 referent 作为参数,另一个还取引用队列作为参数。如果用关联的引用队列创建弱引用,在 referent 成为 GC 候选对象时,这个引用对象(不是 referent)就在引用清除后加入 到引用队列中。之后,应用程序从引用队列提取引用并了解到它的 referent 已被收集,因此可以进行相应的清理活动,如去掉已不在弱集合中的对象的项。(引用队列提供了与 BlockingQueue 同样的出列模式 —— polled、timed blocking 和 untimed blocking。)
WeakHashMap 有一个名为 expungeStaleEntries() 的私有方法,大多数 Map 操作中会调用它,它去掉引用队列中所有失效的引用,并删除关联的映射。清单 7 展示了 expungeStaleEntries() 的一种可能实现。用于存储键-值映射的 Entry 类型扩展了 WeakReference,因此当 expungeStaleEntries() 要求下一个失效的弱引用时,它得到一个 Entry。用引用队列代替定期扫描内容的方法来清理 Map 更有效,因为清理过程不会触及活的项,只有在有实际加入队列的引用时它才工作。
结束语
弱引用和弱集合是对堆进行管理的强大工具,使得应用程序可以使用更复杂的可及性方案,而不只是由普通(强)引用所提供的“要么全部要么没有”可及性。下个月,我们将分析与弱引用有关的软引用,将分析在使用弱引用和软引用时,垃圾收集器的行为。
要让垃圾收集(GC)回收程序不再使用的对象,对象的逻辑 生命周期(应用程序使用它的时间)和对该对象拥有的引用的实际 生命周期必须是相同的。在大多数时候,好的软件工程技术保证这是自动实现的,不用我们对对象生命周期问题花费过多心思。但是偶尔我们会创建一个引用,它在内存中包含对象的时间比我们预期的要长得多,这种情况称为无意识的对象保留(unintentional object retention)。
全局 Map 造成的内存泄漏
无意识对象保留最常见的原因是使用 Map 将元数据与临时对象(transient object)相关联。假定一个对象具有中等生命周期,比分配它的那个方法调用的生命周期长,但是比应用程序的生命周期短,如客户机的套接字连接。需要将一些元数据与这个套接字关联,如生成连接的用户的标识。在创建 Socket 时是不知道这些信息的,并且不能将数据添加到 Socket 对象上,因为不能控制 Socket 类或者它的子类。这时,典型的方法就是在一个全局 Map 中存储这些信息,如清单 1 中的 SocketManager 类所示:
清单 1. 使用一个全局 Map 将元数据关联到一个对象 public class SocketManager { private Map<Socket,User> m = new HashMap<Socket,User>(); public void setUser(Socket s, User u) { m.put(s, u); } public User getUser(Socket s) { return m.get(s); } public void removeUser(Socket s) { m.remove(s); } } SocketManager socketManager; ... socketManager.setUser(socket, user);
这种方法的问题是元数据的生命周期需要与套接字的生命周期挂钩,但是除非准确地知道什么时候程序不再需要这个套接字,并记住从 Map 中删除相应的映射,否则,Socket 和 User 对象将会永远留在 Map 中,远远超过响应了请求和关闭套接字的时间。这会阻止 Socket 和 User 对象被垃圾收集,即使应用程序不会再使用它们。这些对象留下来不受控制,很容易造成程序在长时间运行后内存爆满。除了最简单的情况,在几乎所有情况下找出什么时候 Socket 不再被程序使用是一件很烦人和容易出错的任务,需要人工对内存进行管理。
回页首
找出内存泄漏
程序有内存泄漏的第一个迹象通常是它抛出一个 OutOfMemoryError,或者因为频繁的垃圾收集而表现出糟糕的性能。幸运的是,垃圾收集可以提供能够用来诊断内存泄漏的大量信息。如果以 -verbose:gc 或者 -Xloggc 选项调用 JVM,那么每次 GC 运行时在控制台上或者日志文件中会打印出一个诊断信息,包括它所花费的时间、当前堆使用情况以及恢复了多少内存。记录 GC 使用情况并不具有干扰性,因此如果需要分析内存问题或者调优垃圾收集器,在生产环境中默认启用 GC 日志是值得的。
有工具可以利用 GC 日志输出并以图形方式将它显示出来,JTune 就是这样的一种工具(请参阅 参考资料)。观察 GC 之后堆大小的图,可以看到程序内存使用的趋势。对于大多数程序来说,可以将内存使用分为两部分:baseline 使用和 current load 使用。对于服务器应用程序,baseline 使用就是应用程序在没有任何负荷、但是已经准备好接受请求时的内存使用,current load 使用是在处理请求过程中使用的、但是在请求处理完成后会释放的内存。只要负荷大体上是恒定的,应用程序通常会很快达到一个稳定的内存使用水平。如果在应用程序已经完成了其初始化并且负荷没有增加的情况下,内存使用持续增加,那么程序就可能在处理前面的请求时保留了生成的对象。
清单 2 展示了一个有内存泄漏的程序。MapLeaker 在线程池中处理任务,并在一个 Map 中记录每一项任务的状态。不幸的是,在任务完成后它不会删除那一项,因此状态项和任务对象(以及它们的内部状态)会不断地积累。
清单 2. 具有基于 Map 的内存泄漏的程序 public class MapLeaker { public ExecutorService exec = Executors.newFixedThreadPool(5); public Map<Task, TaskStatus> taskStatus = Collections.synchronizedMap(new HashMap<Task, TaskStatus>()); private Random random = new Random(); private enum TaskStatus { NOT_STARTED, STARTED, FINISHED }; private class Task implements Runnable { private int[] numbers = new int[random.nextInt(200)]; public void run() { int[] temp = new int[random.nextInt(10000)]; taskStatus.put(this, TaskStatus.STARTED); doSomeWork(); taskStatus.put(this, TaskStatus.FINISHED); } } public Task newTask() { Task t = new Task(); taskStatus.put(t, TaskStatus.NOT_STARTED); exec.execute(t); return t; } }
图 1 显示 MapLeaker GC 之后应用程序堆大小随着时间的变化图。上升趋势是存在内存泄漏的警示信号。(在真实的应用程序中,坡度不会这么大,但是在收集了足够长时间的 GC 数据后,上升趋势通常会表现得很明显。)
图 1. 持续上升的内存使用趋势
确信有了内存泄漏后,下一步就是找出哪种对象造成了这个问题。所有内存分析器都可以生成按照对象类进行分解的堆快照。有一些很好的商业堆分析工具,但是找出内存泄漏不一定要花钱买这些工具 —— 内置的 hprof 工具也可完成这项工作。要使用 hprof 并让它跟踪内存使用,需要以 -Xrunhprof:heap=sites 选项调用 JVM。
清单 3 显示分解了应用程序内存使用的 hprof 输出的相关部分。(hprof 工具在应用程序退出时,或者用 kill -3 或在 Windows 中按 Ctrl+Break 时生成使用分解。)注意两次快照相比,Map.Entry、Task 和 int[] 对象有了显著增加。
请参阅 清单 3。
清单 4 展示了 hprof 输出的另一部分,给出了 Map.Entry 对象的分配点的调用堆栈信息。这个输出告诉我们哪些调用链生成了 Map.Entry 对象,并带有一些程序分析,找出内存泄漏来源一般来说是相当容易的。
清单 4. HPROF 输出,显示 Map.Entry 对象的分配点 TRACE 300446: java.util.HashMap$Entry.<init>(<Unknown Source>:Unknown line) java.util.HashMap.addEntry(<Unknown Source>:Unknown line) java.util.HashMap.put(<Unknown Source>:Unknown line) java.util.Collections$SynchronizedMap.put(<Unknown Source>:Unknown line) com.quiotix.dummy.MapLeaker.newTask(MapLeaker.java:48) com.quiotix.dummy.MapLeaker.main(MapLeaker.java:64)
回页首
弱引用来救援了
SocketManager 的问题是 Socket-User 映射的生命周期应当与 Socket 的生命周期相匹配,但是语言没有提供任何容易的方法实施这项规则。这使得程序不得不使用人工内存管理的老技术。幸运的是,从 JDK 1.2 开始,垃圾收集器提供了一种声明这种对象生命周期依赖性的方法,这样垃圾收集器就可以帮助我们防止这种内存泄漏 —— 利用弱引用。
弱引用是对一个对象(称为 referent)的引用的持有者。使用弱引用后,可以维持对 referent 的引用,而不会阻止它被垃圾收集。当垃圾收集器跟踪堆的时候,如果对一个对象的引用只有弱引用,那么这个 referent 就会成为垃圾收集的候选对象,就像没有任何剩余的引用一样,而且所有剩余的弱引用都被清除。(只有弱引用的对象称为弱可及(weakly reachable)。)
WeakReference 的 referent 是在构造时设置的,在没有被清除之前,可以用 get() 获取它的值。如果弱引用被清除了(不管是 referent 已经被垃圾收集了,还是有人调用了 WeakReference.clear()),get() 会返回 null。相应地,在使用其结果之前,应当总是检查 get() 是否返回一个非 null 值,因为 referent 最终总是会被垃圾收集的。
用一个普通的(强)引用拷贝一个对象引用时,限制 referent 的生命周期至少与被拷贝的引用的生命周期一样长。如果不小心,那么它可能就与程序的生命周期一样 —— 如果将一个对象放入一个全局集合中的话。另一方面,在创建对一个对象的弱引用时,完全没有扩展 referent 的生命周期,只是在对象仍然存活的时候,保持另一种到达它的方法。
弱引用对于构造弱集合最有用,如那些在应用程序的其余部分使用对象期间存储关于这些对象的元数据的集合 —— 这就是 SocketManager 类所要做的工作。因为这是弱引用最常见的用法,WeakHashMap 也被添加到 JDK 1.2 的类库中,它对键(而不是对值)使用弱引用。如果在一个普通 HashMap 中用一个对象作为键,那么这个对象在映射从 Map 中删除之前不能被回收,WeakHashMap 使您可以用一个对象作为 Map 键,同时不会阻止这个对象被垃圾收集。清单 5 给出了 WeakHashMap 的 get() 方法的一种可能实现,它展示了弱引用的使用:
清单 5. WeakReference.get() 的一种可能实现 public class WeakHashMap<K,V> implements Map<K,V> { private static class Entry<K,V> extends WeakReference<K> implements Map.Entry<K,V> { private V value; private final int hash; private Entry<K,V> next; ... } public V get(Object key) { int hash = getHash(key); Entry<K,V> e = getChain(hash); while (e != null) { K eKey= e.get(); if (e.hash == hash && (key == eKey || key.equals(eKey))) return e.value; e = e.next; } return null; }
调用 WeakReference.get() 时,它返回一个对 referent 的强引用(如果它仍然存活的话),因此不需要担心映射在 while 循环体中消失,因为强引用会防止它被垃圾收集。WeakHashMap 的实现展示了弱引用的一种常见用法 —— 一些内部对象扩展 WeakReference。其原因在下面一节讨论引用队列时会得到解释。
在向 WeakHashMap 中添加映射时,请记住映射可能会在以后“脱离”,因为键被垃圾收集了。在这种情况下,get() 返回 null,这使得测试 get() 的返回值是否为 null 变得比平时更重要了。
用 WeakHashMap 堵住泄漏
在 SocketManager 中防止泄漏很容易,只要用 WeakHashMap 代替 HashMap 就行了,如清单 6 所示。(如果 SocketManager 需要线程安全,那么可以用 Collections.synchronizedMap() 包装 WeakHashMap)。当映射的生命周期必须与键的生命周期联系在一起时,可以使用这种方法。不过,应当小心不滥用这种技术,大多数时候还是应当使用普通的 HashMap 作为 Map 的实现。
清单 6. 用 WeakHashMap 修复 SocketManager public class SocketManager { private Map<Socket,User> m = new WeakHashMap<Socket,User>(); public void setUser(Socket s, User u) { m.put(s, u); } public User getUser(Socket s) { return m.get(s); } }
引用队列
WeakHashMap 用弱引用承载映射键,这使得应用程序不再使用键对象时它们可以被垃圾收集,get() 实现可以根据 WeakReference.get() 是否返回 null 来区分死的映射和活的映射。但是这只是防止 Map 的内存消耗在应用程序的生命周期中不断增加所需要做的工作的一半,还需要做一些工作以便在键对象被收集后从 Map 中删除死项。否则,Map 会充满对应于死键的项。虽然这对于应用程序是不可见的,但是它仍然会造成应用程序耗尽内存,因为即使键被收集了,Map.Entry 和值对象也不会被收集。
可以通过周期性地扫描 Map,对每一个弱引用调用 get(),并在 get() 返回 null 时删除那个映射而消除死映射。但是如果 Map 有许多活的项,那么这种方法的效率很低。如果有一种方法可以在弱引用的 referent 被垃圾收集时发出通知就好了,这就是引用队列 的作用。
引用队列是垃圾收集器向应用程序返回关于对象生命周期的信息的主要方法。弱引用有两个构造函数:一个只取 referent 作为参数,另一个还取引用队列作为参数。如果用关联的引用队列创建弱引用,在 referent 成为 GC 候选对象时,这个引用对象(不是 referent)就在引用清除后加入 到引用队列中。之后,应用程序从引用队列提取引用并了解到它的 referent 已被收集,因此可以进行相应的清理活动,如去掉已不在弱集合中的对象的项。(引用队列提供了与 BlockingQueue 同样的出列模式 —— polled、timed blocking 和 untimed blocking。)
WeakHashMap 有一个名为 expungeStaleEntries() 的私有方法,大多数 Map 操作中会调用它,它去掉引用队列中所有失效的引用,并删除关联的映射。清单 7 展示了 expungeStaleEntries() 的一种可能实现。用于存储键-值映射的 Entry 类型扩展了 WeakReference,因此当 expungeStaleEntries() 要求下一个失效的弱引用时,它得到一个 Entry。用引用队列代替定期扫描内容的方法来清理 Map 更有效,因为清理过程不会触及活的项,只有在有实际加入队列的引用时它才工作。
清单 7. WeakHashMap.expungeStaleEntries() 的可能实现 private void expungeStaleEntries() { Entry<K,V> e; while ( (e = (Entry<K,V>) queue.poll()) != null) { int hash = e.hash; Entry<K,V> prev = getChain(hash); Entry<K,V> cur = prev; while (cur != null) { Entry<K,V> next = cur.next; if (cur == e) { if (prev == e) setChain(hash, next); else prev.next = next; break; } prev = cur; cur = next; } } }
结束语
弱引用和弱集合是对堆进行管理的强大工具,使得应用程序可以使用更复杂的可及性方案,而不只是由普通(强)引用所提供的“要么全部要么没有”可及性。下个月,我们将分析与弱引用有关的软引用,将分析在使用弱引用和软引用时,垃圾收集器的行为。
发表评论
-
貌似简单的模式-单例模式
2013-10-24 10:01 681翻看设计模式,单例 ... -
人情世故中的设计模式-适配器模式
2013-10-23 10:53 462时不时翻看设计模式,发现有些模式不仅在设计层次有其价值体现, ... -
模板方法-有些活儿,先搭架子再填肉。
2013-10-23 10:51 642张猫猫同学搞广告的,她们整方案多用PPT,分工是这样的,组 ... -
用JMX做运维管理工具
2013-10-10 09:17 0手头有些应用:解密解压 校验拆分 数据装载, 全部是后台进 ... -
一道线程应用的试题
2011-11-22 18:18 1346网上找的,有人说疑似Google的多线程面试题: 启动 ... -
作为一个应用系的程序员,我们该掌握什么?!
2011-06-03 12:45 7971. 业务知识 话说业务知识,那才真该是做企业应用开发人 ... -
FtpClient要设置setSotimeOut!
2011-05-13 15:01 5571如果,如果不设置,可能在执行completePendingCo ... -
不当使用quartz度出现waiting on condition不完全解决思路
2011-05-10 17:40 3693最近发现个问题,不知道是使用不当还是何种原因造成的。 具体内 ... -
不当使用quartz度出现waiting on condition不完全解决思路
2011-05-10 15:12 4不当使用quartz度出现waiting on conditi ... -
转自zerozone oom
2011-05-07 16:46 1022Java的OutOfMemoryError 关键字: out ... -
将HashMap文件化
2011-04-20 11:40 2371( 只是个想法加雏形,实现的很丑陋且效率很低下) ... -
生产者和消费者模型应用
2010-11-29 12:57 854一组生产者线程不断产生数据到生产者队列中,另一组消费者线程定时 ... -
初步设计校验器框架
2010-08-20 16:46 886项目中有一批文十几种 ... -
crontab 笔记
2010-08-10 18:55 651第一列 分钟 1-59 第二列 小时 1-23 第三列 日1- ... -
再次小小的修改common-net中ftp包
2010-05-15 00:13 1017起因:ftp协议八几年定的,那会还没有nat转换,ftp协议中 ... -
dbunit导出数据
2008-11-10 10:22 1776<?xml version="1.0" ...
相关推荐
62.java引用类型的参数传递.zip62.java引用类型的参数传递.zip62.java引用类型的参数传递.zip62.java引用类型的参数传递.zip62.java引用类型的参数传递.zip62.java引用类型的参数传递.zip62.java引用类型的参数传递....
### 浅析Java引用类型和方法参数传递 #### 一、引言 在Java编程语言中,理解数据类型的处理方式对于编写高效、可维护的代码至关重要。本文将深入探讨Java中的引用类型及其如何影响方法参数的传递机制。通过具体实例...
本示例“java引用的demo”将深入探讨四种不同类型的引用:强引用、软引用、弱引用和虚引用。这些引用类型对于理解和优化内存管理至关重要,特别是在Android开发中,因为良好的内存管理能够提升应用性能并防止内存...
Java提供了四种不同级别的引用类型:强引用、软引用、弱引用和虚引用(幻象引用),每种引用类型在内存管理中有不同的作用。 1. **强引用(Strong Reference)** - 强引用是最常见的引用类型,即通常的变量赋值。...
这个“java 引用相关文档”可能包含了深入探讨Java引用的各种类型及其特性的内容。在这里,我们将详细讲解Java中的四种引用类型:强引用、软引用、弱引用和虚引用,以及它们在内存管理和垃圾收集中的作用。 1. **强...
### Java引用变量传递诡异之处详解 #### 背景与问题描述 在Java编程语言中,对于引用类型变量的理解及其实现方式一直是开发者们容易混淆的地方。尤其是当涉及到方法调用过程中引用变量的传递时,更是如此。本文将...
在这里,我们将对Java引用类型的一些关键知识点进行详细的阐述。 1. 类(Class):Java是一种面向对象的语言,类是创建对象的蓝图。它定义了对象的属性(数据成员)和行为(方法)。通过定义类,我们可以创建具有...
多态性是Java引用类型的一个关键特性,允许你用父类引用指向子类对象。这使得代码更加灵活,能够处理不同类型的对象。例如: ```java Animal animal = new Dog(); // 父类引用指向子类实例 animal.eat(); // 调用...
Java引用类型总结 Java从1.2版本开始引入了四种引用,分别是强引用(StrongReference)、软引用(SoftReference)、弱引用(WeakReference)和虚引用(PhantomReference)。这四种引用的级别由高到低依次为:强引用...
关于C语言指针和Java引用的思考 该资源主要讨论了C语言指针和Java引用的概念、实现、差异和应用场景。下面是对该资源的详细分析和知识点总结: 1. C语言指针: C语言中,函数的内存都是分配在栈中的,当该函数...
## 开发环境准备: ###开发使用到的软件和工具: * Jdk8、mysql5.7、libarcsoft_face.dll(so)、libarcsoft_face_engine.dll(so)、libarcsoft_face_engine_jni.dll(so)、idea * 注:libarcsoft_face.... * 本地配置:
Java 引用队列和虚引用实例分析 Java 引用队列和虚引用是 Java 语言中两种重要的引用类型,它们在程序中扮演着不同的角色。引用队列由 `ReferenceQueue` 类表示,用于保存被回收后对象的引用,而虚引用则是弱引用的...
Java 中的基本类型和引用类型变量的区别 Java 中的基本类型和引用类型变量是两个不同的概念,它们在内存分配、变量赋值和函数传递等方面有着本质的区别。 基本类型是 Java 中的八种基本类型,包括 byte、short、...
Java 引用类型的参数也是值传递 Java 中的参数传递有两种类型:基本类型和引用类型。基本类型参数是典型的值传递,而引用类型参数则是将引用变量的值传递给形参,实参和形参指向了同一个对象。如果利用形参进行操作...
Java中的引用问题主要涉及到对象作为参数传递时的行为。在Java中,有两类数据类型:基本类型(如int、char)和引用类型(如类实例、数组)。对于基本类型,Java采用值传递的方式,而对于引用类型,则是传递引用的...
基于JVM存储架构对Java引用传递的解析.pdf
### Java中对象与引用 在Java编程中,深入理解对象与引用的概念对于掌握面向对象编程至关重要。本文将从以下几个方面详细解析对象与引用的基本概念及其相互间的关系。 #### 一、对象与引用的基础概念 **对象**: ...
在Java中,对象的引用方式对于内存管理和对象生命周期的控制至关重要。从JDK 1.2版本开始,Java引入了四种不同级别的引用:强引用(Strong Reference)、软...希望本文能够为您提供在Java引用类型的使用上清晰的指导
在Java编程语言中,理解和掌握引用、对象以及属性等数据类型知识是至关重要的。这些概念构成了Java程序的基础,让我们深入探讨一下。 1. 引用(References): 在Java中,引用是一种变量,它存储了对象在内存中的...
在Java编程语言中,方法引用是一种特殊的语法构造,它允许我们直接引用已有方法,而无需在lambda表达式中重新定义。方法引用的概念是随着Java 8的发布引入的,它是函数式编程特性的一部分,旨在简化代码并提高可读性...