-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement Deflater / Inflater Object Pool #300
Comments
+1 for using the proposed Deflator pool in GzipHandler, too. In our production app using Jetty 9.4.9.v20180320 (which includes #1429), we experienced the following error:
That is, Java fails to allocate enough native memory, and we think that this is caused by GzipHandler According to our analysis, the problem is not that calling In principle, the Deflator could then be garbage-collected, so that its memory would be freed in Therefore, we worked around this issue by subclassing GzipHandler and calling public class NonRecyclingGzipHandler extends GzipHandler {
@Override
public void recycle(Deflater deflater) { deflater.end(); }
} This seems to prevent memory leaks, but of course is not ideal. A proper pool of Deflaters as proposed here, where instances can be shared by multiple threads, would certainly be a better solution. |
@mperktold yet, object pooling has its own problems. For your case, how would you size the object pool? It's easy to have an infinite one, but that's not good. Let's imagine you have a peak of requests and you fill the object pool to its capacity, but then you go idle. Is it ok to keep around all those |
@gregw @joakime what's your thought on this? We can create a
Or, we can create a generic
No matter which choice, |
See also #2579. |
Those are some good questions. Here are some opinions on them, but keep in mind that, unlike you guys, I'm not an expert in this field. 😉 The pool should be sized and configured by passing parameters to the constructor, and then the configured pool is given to new GzipHandler(new DeflaterPool(lowerBound, upperBound, timeout, TimeUnit.SECONDS, scheduler)); A generic pool would be fine as well. The point is: the programmer should be able to configure the pool. It would also be nice to have some sensible default values for the configuration, just as in The pool should clean up Regarding the pool's capacity: I would say that the number of Now if I were to choose a value for the upper bound, I would try to choose it high enough such that it is unlikely to be reached under normal load, but also not too high such that when it is reached under high load, there is still sufficient memory. Probably, some experiments are needed to find a good upper bound. One possibility would be to set it equal to the upper bound of the |
@joakime brought a good point that |
@gregw summary of hangout with @sbordet We shouldn't be using Is a DeflaterPool actually needed when a new Deflater could be created and ended each time with multiple threads in parallel avoiding all potential lock contention on the pool? Currently |
@lachlan-roberts see above my comment on recycling The problem is that the moment thread1 |
@sbordet @lachlan-roberts When the "pool" was implemented, it was certainly a big performance win over creating new Deflaters each time... but that was a long time ago, so the JVM may well have changed significantly. @lachlan-roberts Can you do a Simple JMH test that compares (creating a new Deflator, configuring it and compressing a single character) vs (taking a Deflator from a simple ConcurrentLinkedList pool and compressing a single character). The |
Removed the ThreadLocal pooling of deflaters in GzipHandler in favour of a new DeflaterPool class GzipHttpOutputInterceptor.GzipBufferCB now recycles the Deflater in onCompleteFailure() added benchmark for the DeflaterPool Signed-off-by: lachan-roberts <lachlan@webtide.com>
Removed the ThreadLocal pooling of deflaters in GzipHandler in favour of a new DeflaterPool class GzipHttpOutputInterceptor.GzipBufferCB now recycles the Deflater in onCompleteFailure() added benchmark for the DeflaterPool Signed-off-by: lachan-roberts <lachlan@webtide.com>
Removed the ThreadLocal pooling of deflaters in GzipHandler in favour of a new DeflaterPool class GzipHttpOutputInterceptor.GzipBufferCB now recycles the Deflater in onCompleteFailure() added benchmark for the DeflaterPool Signed-off-by: lachan-roberts <lachlan@webtide.com>
Changes from review Signed-off-by: lachan-roberts <lachlan@webtide.com>
Changes from review Signed-off-by: lachan-roberts <lachlan@webtide.com>
Changes from review allow negative capacity to mean no limit on the pool size added mod file and xml changes Signed-off-by: lachan-roberts <lachlan@webtide.com>
Changes from review allow negative capacity to mean no limit on the pool size added mod file and xml changes Signed-off-by: lachan-roberts <lachlan@webtide.com>
This looks great, thanks! 😃 |
Actually, I think with current design it's not possible to implement Inflater/Deflater pool for websocket extensions. The thing is that currently websockets try to comply with More on that here https://www.igvita.com/2013/11/27/configuring-and-optimizing-websocket-compression/ Currently websocket extension maintain one Inflater/Deflater pair per one websocket connection. They both are (should be?) finalized on the end of websocket connection, as stated here: Usage of Deflater/Inflater pools will break current design and break current compatibility with RFC7692. Something clearly should be done with websockets, as usage of compression still produces memory leaks. But this ticket should be closed, IMO) |
The problem lies in the JVM. Using a Inflater/Deflater pool would require buckets that correspond to the same "configuration" of a Deflater/Inflater (compression level, wrap mode, flush mode). |
So you're saying, the leak could be fixed just by using Inflater/Deflater pool in CompressionExtension? Well, we could give it at least a try, since naive implementation is not very hard... |
@joakime did I get you right? https://github.com/eclipse/jetty.project/pull/3518/files |
Just a couple of references to current talk from zlib manual:
|
Ok, some time passed, we got some results on testing that. First: I guess it's inevitable, but if you use java.util.zip you have to move to java11, as they fixed finalizer issues with those objects there (https://bugs.openjdk.java.net/browse/JDK-8187485) The memory still does not return to OS, but this is due nature of G1 garbage collection -- a rather hearsay, but currently we got nothing better to explain this. That said, full explicit GC helps. Meaning, if you have java11, explicit GC will gather all the memory even if you use current implementation of PerMessageDeflate. |
The attempt to implement Deflater/Inflater pools for PerMessageDeflate is here: #3518 It also would address memory leaks, even for jdk8 |
Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
Issue #300 - Deflater / Inflater Object Pool
Issue #300 - Deflater / Inflater Object Pool (Jetty-10)
Work has been completed in |
Bug #295 has exposed a JVM bug related to the permessage-deflate websocket extension.
To resolve it, a Deflater / Inflater Object Pool should be created and used by WebSocket.
With potential use by GzipHandler as well.
The text was updated successfully, but these errors were encountered: