【问题标题】:Threads blocked on reading Zip file读取 Zip 文件时被阻止的线程
【发布时间】:2017-11-01 16:48:13
【问题描述】:

我们的 Java 单体应用存在生产问题。用户抱怨速度慢和网站不可用。线程转储显示大约 400 个线程被下面的线程获得的锁阻塞。

所有这些阻塞线程与阻塞线程(最后一帧 +- 1)具有几乎相同的堆栈跟踪 - 尝试从应用程序 JAR 文件加载 VAADIN 资源文件。

这是否意味着线程在从 JAR 中读取静态文件时挂起?而其他人正在等待一个线程完成阅读它?

任何人都知道为什么会发生这种情况以及我们如何防止这种情况发生?

Java 版本:1.8.0_131

Jetty 版本:9.2.z-SNAPSHOT

"qtp489279267-42356" #42356 prio=5 os_prio=0 tid=0x00007fe7e4054800 nid=0x6f37 waiting for monitor entry [0x00007fe776831000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at java.util.zip.ZipFile$ZipEntryIterator.hasNext(ZipFile.java:492)
    - waiting to lock <0x0000000700002e80> (a sun.net.www.protocol.jar.URLJarFile)
    at java.util.zip.ZipFile$ZipEntryIterator.hasMoreElements(ZipFile.java:488)
    at java.util.jar.JarFile$JarEntryIterator.hasNext(JarFile.java:253)
    at java.util.jar.JarFile$JarEntryIterator.hasMoreElements(JarFile.java:262)
    at org.eclipse.jetty.util.resource.JarFileResource.exists(JarFileResource.java:191)
    at org.eclipse.jetty.webapp.WebAppContext.getResource(WebAppContext.java:372)
    at org.eclipse.jetty.webapp.WebAppContext$Context.getResource(WebAppContext.java:1459)
    at com.vaadin.terminal.gwt.server.AbstractApplicationServlet.serveStaticResourcesInVAADIN(AbstractApplicationServlet.java:1276)
    at com.vaadin.terminal.gwt.server.AbstractApplicationServlet.serveStaticResources(AbstractApplicationServlet.java:1246)
    at com.vaadin.terminal.gwt.server.AbstractApplicationServlet.service(AbstractApplicationServlet.java:423)
    at example.ApplicationServlet.service(ApplicationServlet.java:37)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
    at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:808)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1669)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:186)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652)
    at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:585)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)
    at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:577)
    at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:223)
    at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1127)
    at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:515)
    at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:185)
    at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1061)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
    at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:110)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97)
    at org.eclipse.jetty.server.Server.handle(Server.java:499)
    at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:310)
    at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:257)
    at org.eclipse.jetty.io.AbstractConnection$2.run(AbstractConnection.java:540)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:635)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:555)
    at java.lang.Thread.run(Thread.java:748)

【问题讨论】:

    标签: java multithreading jar jetty thread-dump


    【解决方案1】:

    你可能猜对了,你似乎面临着争执。

    我们可以从两个相关的角度来看待它:

    1. 锁争用
    2. 它可能不适用于当前用例,但也可能是负载下的生产系统中的问题:I/O 争用。

    到目前为止,我们从您的堆栈跟踪中得到的症状似乎是锁争用。

    您似乎在同时阅读的多个阅读器之间共享相同的文件(在 Java ZipFile 对象的意义上)。这带来了 缓存 的想法 - 尽管根据您的用例来考虑它可能是完全错误的,例如如果您读取大小超过 1 GB 的文件。

    所以,了解一下会很有用

    1. 您可以读取的文件总数;
    2. 这些文件的中值和最大大小;
    3. 这些文件的平均 TTL,即它们一旦加载到内存中需要多长时间;

    如果我们在任何给定时间段内都有合理数量的数据要缓存(即,我们不会面临来自客户的需求激增,这会使我们需要在几秒钟内获取 1 TB 的新数据)并管理一个“相对较小”的 ad-hoc 软件系统,一个简单的解决方案可能是缓存。

    【讨论】:

    • 好吧,所有堆栈跟踪都相同 - 通过 Vaadin 尝试从 JAR 中的 VAADIN 文件夹加载资源(静态文件)。我的主要问题是,它是如何发生的,因为通常客户端(浏览器)经常读取静态文件,并且读取 JAR 文件如何使线程长时间挂起? (实际上只是在“存在”方法上)
    • 为什么FS会超载,当同步在Jar文件级别完成时,即(如果我理解正确的话)多个进程无法并行读取jar,但它们正在相互等待,否则不会有 400 个线程处于阻塞状态。无论如何,大约有 20 个静态文件。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-03
    • 2014-07-12
    • 2016-10-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多