Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/rest/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 线程本地资源泄漏和WeakReference_Java_Thread Local_Weak References - Fatal编程技术网

Java 线程本地资源泄漏和WeakReference

Java 线程本地资源泄漏和WeakReference,java,thread-local,weak-references,Java,Thread Local,Weak References,我对它的有限理解是它有。我认为这个问题可以通过正确使用WITHREADLOCAL来解决(尽管我可能误解了这一点)。我只想了解一个模式或示例,用于正确使用WITHREADLOCAL with WEAKREFENCE(如果存在的话)。例如,在这个代码片段中,WeakReference将在哪里引入 static class DateTimeFormatter { private static final ThreadLocal<SimpleDateFormat> DATE_PARS

我对它的有限理解是它有。我认为这个问题可以通过正确使用WITHREADLOCAL来解决(尽管我可能误解了这一点)。我只想了解一个模式或示例,用于正确使用WITHREADLOCAL with WEAKREFENCE(如果存在的话)。例如,在这个代码片段中,WeakReference将在哪里引入

static class DateTimeFormatter {
    private static final ThreadLocal<SimpleDateFormat> DATE_PARSER_THREAD_LOCAL = new ThreadLocal<SimpleDateFormat>() {
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat("yyyy/MM/dd HH:mmz");
        }
    };
    public String format(final Date date) {
        return DATE_PARSER_THREAD_LOCAL.get().format(date);
    }
    public Date parse(final String date) throws ParseException
    {
      return DATE_PARSER_THREAD_LOCAL.get().parse(date);
    }
}
protected ThreadLocal myThreadLocal = new ThreadLocal(); public void doFilter (ServletRequest req, ServletResponse res, chain) throws IOException, ServletException { try { // Set ThreadLocal (eg. to store heavy computation result done only once per request) myThreadLocal.set(context()); chain.doFilter(req, res); } finally { // Important : cleanup ThreaLocal to prevent memory leak userIsSmartTL.remove(); } }
静态类DateTimeFormatter{
私有静态final ThreadLocal DATE\u PARSER\u THREAD\u LOCAL=new ThreadLocal(){
受保护的SimpleDataFormat初始值(){
返回新的SimpleDataFormat(“yyyy/MM/dd HH:mmz”);
}
};
公共字符串格式(最终日期){
返回日期\u解析器\u线程\u LOCAL.get().format(日期);
}
公共日期解析(最终字符串日期)引发ParseException
{
返回日期\u解析器\u线程\u LOCAL.get().parse(日期);
}
}
不应该有这样的问题

protected ThreadLocal myThreadLocal = new ThreadLocal(); public void doFilter (ServletRequest req, ServletResponse res, chain) throws IOException, ServletException { try { // Set ThreadLocal (eg. to store heavy computation result done only once per request) myThreadLocal.set(context()); chain.doFilter(req, res); } finally { // Important : cleanup ThreaLocal to prevent memory leak userIsSmartTL.remove(); } } 一个线程的ThreadLocal引用被定义为只在对应线程处于活动状态时才存在(参见javadoc)——或者换句话说,一旦线程不处于活动状态,如果ThreadLocal是该对象的唯一引用,那么该对象就有资格进行垃圾收集

protected ThreadLocal myThreadLocal = new ThreadLocal(); public void doFilter (ServletRequest req, ServletResponse res, chain) throws IOException, ServletException { try { // Set ThreadLocal (eg. to store heavy computation result done only once per request) myThreadLocal.set(context()); chain.doFilter(req, res); } finally { // Important : cleanup ThreaLocal to prevent memory leak userIsSmartTL.remove(); } }
所以,要么你发现了一个真正的bug,应该报告它,要么你做错了什么

再加上@Neil Coffey所说的,只要您的ThreadLocal实例是静态的,这就不应该是一个问题。因为您一直在静态实例上调用get(),所以它应该始终保持对简单日期格式化程序的相同引用。因此,正如Neil所说,当线程终止时,simple date formatter的唯一实例应该可以进行垃圾收集

protected ThreadLocal myThreadLocal = new ThreadLocal(); public void doFilter (ServletRequest req, ServletResponse res, chain) throws IOException, ServletException { try { // Set ThreadLocal (eg. to store heavy computation result done only once per request) myThreadLocal.set(context()); chain.doFilter(req, res); } finally { // Important : cleanup ThreaLocal to prevent memory leak userIsSmartTL.remove(); } }
如果您有数字或其他形式的调试表明这会引入资源问题,那么这就是另一回事了。但我相信这应该不会是一个问题。

我猜您正在跳过这些障碍,因为SimpleDateFormat不是线程安全的

protected ThreadLocal myThreadLocal = new ThreadLocal(); public void doFilter (ServletRequest req, ServletResponse res, chain) throws IOException, ServletException { try { // Set ThreadLocal (eg. to store heavy computation result done only once per request) myThreadLocal.set(context()); chain.doFilter(req, res); } finally { // Important : cleanup ThreaLocal to prevent memory leak userIsSmartTL.remove(); } }
虽然我知道我没有解决你上面的问题,但我可以建议你看看你的约会/时间工作吗?Joda有一个线程安全的日期/时间格式化机制。您也不会浪费时间学习JoDA API,因为它是新的标准日期/时间API建议的基础。

< P>在您的示例中,使用线程本地根本不存在问题。 protected ThreadLocal myThreadLocal = new ThreadLocal(); public void doFilter (ServletRequest req, ServletResponse res, chain) throws IOException, ServletException { try { // Set ThreadLocal (eg. to store heavy computation result done only once per request) myThreadLocal.set(context()); chain.doFilter(req, res); } finally { // Important : cleanup ThreaLocal to prevent memory leak userIsSmartTL.remove(); } } 当线程局部变量被设置为类加载器加载的实例并在以后卸载时,线程局部变量(以及单例)就会成为一个问题。servlet容器中的典型情况(如tomcat):

protected ThreadLocal myThreadLocal = new ThreadLocal(); public void doFilter (ServletRequest req, ServletResponse res, chain) throws IOException, ServletException { try { // Set ThreadLocal (eg. to store heavy computation result done only once per request) myThreadLocal.set(context()); chain.doFilter(req, res); } finally { // Important : cleanup ThreaLocal to prevent memory leak userIsSmartTL.remove(); } }
  • webapp在请求处理期间将线程设置为本地线程
  • 线程由容器管理
  • 应取消部署webapp
  • webapp的类加载器不能被丢弃,因为还剩下一个引用:从本地线程到实例,再到它的类,再到它的类加载器
  • Singleton也是如此(webapp提供的java.sql.driverManager和JDBC驱动程序)

    protected ThreadLocal myThreadLocal = new ThreadLocal(); public void doFilter (ServletRequest req, ServletResponse res, chain) throws IOException, ServletException { try { // Set ThreadLocal (eg. to store heavy computation result done only once per request) myThreadLocal.set(context()); chain.doFilter(req, res); } finally { // Important : cleanup ThreaLocal to prevent memory leak userIsSmartTL.remove(); } }
    因此,特别是在您无法完全控制的环境中,请避免此类事情

    ThreadLocal
    在内部使用
    WeakReference
    。如果
    ThreadLocal
    没有被强引用,它将被垃圾收集,即使不同的线程都有通过该
    ThreadLocal
    存储的值

    protected ThreadLocal myThreadLocal = new ThreadLocal(); public void doFilter (ServletRequest req, ServletResponse res, chain) throws IOException, ServletException { try { // Set ThreadLocal (eg. to store heavy computation result done only once per request) myThreadLocal.set(context()); chain.doFilter(req, res); } finally { // Important : cleanup ThreaLocal to prevent memory leak userIsSmartTL.remove(); } } 此外,
    ThreadLocal
    值实际上存储在
    线程中;如果线程死亡,则通过
    ThreadLocal
    收集与该线程关联的所有值

    protected ThreadLocal myThreadLocal = new ThreadLocal(); public void doFilter (ServletRequest req, ServletResponse res, chain) throws IOException, ServletException { try { // Set ThreadLocal (eg. to store heavy computation result done only once per request) myThreadLocal.set(context()); chain.doFilter(req, res); } finally { // Important : cleanup ThreaLocal to prevent memory leak userIsSmartTL.remove(); } } 如果最后一个类成员是
    ThreadLocal
    ,则这是一个强引用,在卸载该类之前无法收集该引用。但这是任何类成员的工作方式,不被视为内存泄漏

    protected ThreadLocal myThreadLocal = new ThreadLocal(); public void doFilter (ServletRequest req, ServletResponse res, chain) throws IOException, ServletException { try { // Set ThreadLocal (eg. to store heavy computation result done only once per request) myThreadLocal.set(context()); chain.doFilter(req, res); } finally { // Important : cleanup ThreaLocal to prevent memory leak userIsSmartTL.remove(); } }
    更新:仅当存储在
    ThreadLocal
    中的值强烈引用了
    ThreadLocal
    类循环引用时,引用的问题才会起作用

    protected ThreadLocal myThreadLocal = new ThreadLocal(); public void doFilter (ServletRequest req, ServletResponse res, chain) throws IOException, ServletException { try { // Set ThreadLocal (eg. to store heavy computation result done only once per request) myThreadLocal.set(context()); chain.doFilter(req, res); } finally { // Important : cleanup ThreaLocal to prevent memory leak userIsSmartTL.remove(); } }
    在这种情况下,值(a
    SimpleDateFormat
    )没有向后引用
    ThreadLocal
    。此代码中没有内存泄漏。

    我意识到这并不是严格地回答您的问题,但作为一般规则,我不建议在请求/交互结束时没有明确中断的情况下使用
    ThreadLocal
    。经典的做法是在servlet容器中做这类事情,乍一看似乎很好,但由于线程是池化的,因此即使在处理每个请求之后,
    ThreadLocal
    仍然挂在资源上,这就成了一个问题

    protected ThreadLocal myThreadLocal = new ThreadLocal(); public void doFilter (ServletRequest req, ServletResponse res, chain) throws IOException, ServletException { try { // Set ThreadLocal (eg. to store heavy computation result done only once per request) myThreadLocal.set(context()); chain.doFilter(req, res); } finally { // Important : cleanup ThreaLocal to prevent memory leak userIsSmartTL.remove(); } } 建议:

    protected ThreadLocal myThreadLocal = new ThreadLocal(); public void doFilter (ServletRequest req, ServletResponse res, chain) throws IOException, ServletException { try { // Set ThreadLocal (eg. to store heavy computation result done only once per request) myThreadLocal.set(context()); chain.doFilter(req, res); } finally { // Important : cleanup ThreaLocal to prevent memory leak userIsSmartTL.remove(); } }
  • 对每个交互使用类似包装的过滤器,以清除每个交互结束时的ThreadLocal
  • 您可以使用SimpleDateFormat like或Joda的替代方案,正如有人已经建议的那样
  • 只要在每次需要时创建一个新的SimpleDataFormat即可。我知道这似乎是浪费,但在大多数应用程序中,您不会注意到其中的差异

  • 使用后,请在本地清理线程,并添加servlet筛选器:

    protected ThreadLocal myThreadLocal = new ThreadLocal(); public void doFilter (ServletRequest req, ServletResponse res, chain) throws IOException, ServletException { try { // Set ThreadLocal (eg. to store heavy computation result done only once per request) myThreadLocal.set(context()); chain.doFilter(req, res); } finally { // Important : cleanup ThreaLocal to prevent memory leak userIsSmartTL.remove(); } } 受保护的ThreadLocal myThreadLocal=新的ThreadLocal(); public void doFilter(ServletRequest-req、ServletResponse-res、chain)抛出IOException、ServletException{ 试一试{ //设置ThreadLocal(例如,每个请求只存储一次繁重的计算结果) myThreadLocal.set(context()); 链式过滤器(要求、恢复); }最后{ //重要提示:清理ThreaLocal以防止内存泄漏 userIsSmartTL.remove(); } }
    上面的代码示例没有问题,因为它在静态变量中使用ThreadLocal

    protected ThreadLocal myThreadLocal = new ThreadLocal(); public void doFilter (ServletRequest req, ServletResponse res, chain) throws IOException, ServletException { try { // Set ThreadLocal (eg. to store heavy computation result done only once per request) myThreadLocal.set(context()); chain.doFilter(req, res); } finally { // Important : cleanup ThreaLocal to prevent memory leak userIsSmartTL.remove(); } } 当您将ThreadLocal实例初始化为非静态变量时,会出现ThreadLocal内存泄漏的一个问题。当包含该变量的对象被垃圾回收时,ThreadLocal的引用将保留在线程中。如果您随后在某种循环中实例化并使用许多ThreadLocals,您将得到