问题
Issue: I had a rest client that was supposed to send request to a URL. The java application was a standalone java Application ( started with java -jar ) and then on exit all application context were closed. but the application would end but not shutdown and the PID remained for the java process. On each request I was doing an async call and getting the response in an InvocationCallback implementation.
client = new ResteasyClientBuilder().connectTimeout(timeout, TimeUnit.SECONDS).readTimeout(timeout, TimeUnit.SECONDS).httpEngine(engine).build();
ResteasyWebTarget resteasyWebTarget = client.target(url));
Future<Response> response = resteasyWebTarget.
request().
headers(headers).
async().
get(restResponseCallback);
Even after one test call to test URL my app would not close Even though all contexts were closed.
The application contexts were closed as described in Spring ApplicationContext.close() doesnot shut down application
回答1:
the issue in short was that i was creating a new client on each request.
I "closed" the application and then looked for the ghost PID of the java processn that always hung after a shutdown. even though the application was " shutdown" but the PID remained, so I did the jmap of the PID When jmap didnt tell me anything about a ghost resource on an advice of a senior i tried jstack for the pid.
I saw this resource hanging (or similar) in short it was stuck on LinkedBlockingqueue.take().
"pool-17-thread-1" #50 prio=5 os_prio=64 tid=0x0000000101758000 nid=0x5f waiting on condition [0xffffffff3f4fe000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000007bf948000> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
I noticed that there will be as many entries of the same stack as many tests (requests) i did.
I had some LinkedBlockingQueues of my own so i removed take() to poll using an answer on stackoverflow ( i cannot find the link now) . but it was not one of ours. So i suspected restEasy using a LBQueue underneath. the debug would only show me a thread that would do that but didnt give me where it initiated from.
I read up on stackoverflow and in some resteasy mail groups there was this issue but no solution. I tried to change my approach and came to the conclusion after reading the javadoc of the parent of RestEasyClient
Clients are heavy-weight objects that manage the client-side communication infrastructure. Initialization as well as disposal of a {@code Client} instance may be a rather expensive operation. It is therefore advised to construct only a small number of {@code Client} instances in the application. Client instances must be {@link
close() properly closed before being disposed to avoid leaking resources.
(should have read what i was using to begin with ) So I changed my implementation to create only one client and many client.targets
ResteasyWebTarget resteasyWebTarget = client.target(url);
this removed the resource leak. technically there is no resource leak in JS RX or Resteasy it is just how implemented it. hope this helps some one who may have done the same mistake I did and is baffled by a resource that just wouldn't Die.
Some really nice posts that can help and refine it more: Memory issue with JAX RS using jersey
Both the answers are very valuable in my opinion
来源:https://stackoverflow.com/questions/51831789/resteasyclient-jax-rs-possible-resource-leak-if-not-done-correctly