-
Notifications
You must be signed in to change notification settings - Fork 54
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
Bug#180_Stuck_threads_due_to_WeakHashMap #181
base: main
Are you sure you want to change the base?
Bug#180_Stuck_threads_due_to_WeakHashMap #181
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. I added a few comments.
private final WeakHashMap<ClassLoader, SoftReference<List<ValidationProvider<?>>>> providersPerClassloader = | ||
new WeakHashMap<>(); | ||
private final Map<ClassLoader, SoftReference<List<ValidationProvider<?>>>> providersPerClassloader = | ||
Collections.synchronizedMap(new WeakHashMap<ClassLoader, SoftReference<List<ValidationProvider<?>>>>()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure you're not doing something wrong in your code? Because write operations to this map should be extremely rare and if you have issues, I wonder if you are not doing something wrong.
That being said, I think the change makes sense.
@yrodiere could you have a look at this change too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gsmet thank you for the comments
there are few issues related to WeakHashMap not being synchronized , I have checked with JDK team too
eclipse-ee4j/metro-jax-ws#61
spring-projects/spring-loaded#194
https://www.adam-bien.com/roller/abien/entry/endless_loops_in_unsychronized_weakhashmap
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not saying this is not an issue. I'm saying that you shouldn't have it as you shouldn't call the method accessing this often: my wild guess is that, if you're hitting this, you are creating ValidatorFactory
instances far too often. Your ValidatorFactory
should be created once and for all and shared. Creating a new ValidatorFactory
each time will cost you as it won't have the bean metadata cached.
@Test | ||
public void testWeakHashMapSynchronization() { | ||
try { | ||
Validation validation = new Validation(); | ||
GenericBootstrap f1 = Validation.byDefaultProvider(); | ||
System.out.println(f1.getClass().getDeclaredField("defaultResolver")); | ||
Class<?>[] classes = validation.getClass().getDeclaredClasses(); | ||
for (int i = 0; i < classes.length; i++) { | ||
if (classes[i].getSimpleName().equals("GetValidationProviderListAction")) { | ||
Field field = classes[i].getDeclaredField("providersPerClassloader"); | ||
field.setAccessible(true); | ||
Constructor<?> constructor = classes[i].getDeclaredConstructor(); | ||
constructor.setAccessible(true); | ||
Object obj = constructor.newInstance(); | ||
assertTrue(field.get(obj).getClass().getName().equals("java.util.Collections$SynchronizedMap")); | ||
} | ||
} | ||
} catch (Exception e) { | ||
e.printStackTrace(); | ||
fail(); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks but you can drop the test, it really has no value. If we were doing that for everything, it would just be a big nightmare to maintain.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
okay i will remove the test
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure how this is supposed to solve the problem exactly.
As far as I can see, the code that accesses this map is already synchronized:
private synchronized List<ValidationProvider<?>> getCachedValidationProviders(ClassLoader classLoader) {
SoftReference<List<ValidationProvider<?>>> ref = providersPerClassloader.get( classLoader );
return ref != null ? ref.get() : null;
}
private synchronized void cacheValidationProviders(ClassLoader classLoader, List<ValidationProvider<?>> providers) {
providersPerClassloader.put( classLoader, new SoftReference<>( providers ) );
}
So, in theory, adding one more level of synchronization wouldn't hurt (except performance-wise), but I don't see how it could help?
Also... I'd really like more info about #180. It mentions "stuck threads", but if you're talking about a deadlock, there should be multiple threads involved, and I only see one in the issue description... ?
Here (getCachedValidationProviders) the synchronization is on class monitor - this is not going to help this situation, the HashMap itself is not synchronized & that is leading to the issue. |
I have updated the bug with stack of other deadlocked thread |
Those two methods are not synchronizing on the class, but on the That being said, I see there's a static
I'm not disputing the fact that What I'm arguing is that we already synchronize access to that map, so adding But as I said above, the |
Apart from @yrodiere 's comment, I would repeat what I initially said: these particular methods should generally be called once in the lifetime of an application. Not saying that we shouldn't fix a synchronization issue if we end up finding one - for now, it's not clear if there is one. But my point is that even if we had one, you shouldn't experience it, except if you're doing very weird things that I wouldn't recommend if you want your application to perform well. |
I have requested client to confirm on this API usage. I will check that. |
Thanks, I have updated the stack, the issue is not happening during clearCache. |
FullThreadDump_Validtion_Hashmap.txt gsmet yrodiere |
Thanks. To clarify: the only reason I'm reluctant to merge this patch is that it doesn't seem to fix anything. I'd like to understand how the patch can fix anything, and ideally also the problem itself. As you can clearly see from your own thread dump, access to the hash map is already protected by a lock:
And after #182, that lock is being used by every method that could possibly access that map. So wrapping the map with
As far as I understand,
That's good. Did you also check that Be aware that this code will be invoked reflectively, see validation/src/main/java/jakarta/validation/Validation.java Lines 157 to 167 in 1aac339
Also, considering the history behind Finally... did you try to run your application with an updated bean-validation JAR that includes my patch (#182), to see if the problem still occurs? |
this is a known issue about WeakHashMap, Here are few issues where WeakHashMap is not synchronized is reported Here is the problem as it showed up in Nashorn. The Java SE API publicizes that WeakHashMap is not synchronized. Hence the fix is suggested at Validation. This been suggested after review from JDK developers. Weblogic is not calling clearCache using reflection, We are unable to reproduce the issue at will, hence unable confirm the fix of #182 |
Yes, I've already followed your links and I understand the problem. The question is whether your patch would solve it, considering we already have an equivalent solution (
Great minds think alike, then, because they just synchronized all methods accessing the
Indeed. We do synchronize our own calls to
... whose methods accessing the
Calling
Ok, so as I understand it, you can't confirm my patch solves your problem. I infer you cannot confirm that your patch solves the problem either. That would be fine if I could understand how on earth your patch could solve the problem, but I don't. Let's hope someone else does and will be able to merge your patch. |
No description provided.