I am creating an interface for executing methods concurrently, while abstracting away the synchronization details (To swap for a distributed implementation when needed). I've created a single jvm implementation that allows Strings to be used as the mutex by storing them in a map to ensure one reference is used, even if Strings of different references are passed in. The concurrency seems to work fine, however I was surprised to see that the test was showing the reference count is never decreasing. I assumed using WeakValues() would be enough to prevent memory leaks, but it seems that is not the case. Can anyone point out what could be causing this leak?
public class SynchronousMethodExecutorSynchronizedImpl implements ISynchronousMethodExecutor {
// mutex map to provide string references
final Map<String, String> mutexMap = new MapMaker()
.weakValues()
.makeComputingMap(
new Function<String, String>() {
#Override
public String apply(String id) {
return id;
}
});
#Override
public Object doSynchronousMethod(String domain, String id, ISynchronousMethod synchronousMethod) {
synchronized(mutexMap.get(domain + "." + id))
{
return synchronousMethod.execute();
}
}
}
Here is the test that is failing at the very last assertion:
public class SynchronousMethodExecutorSynchronizedImplTest extends TestCase {
int counter;
SynchronousMethodExecutorSynchronizedImpl methodExecutor;
#Override
public void before() throws Exception {
super.before();
methodExecutor = new SynchronousMethodExecutorSynchronizedImpl();
}
#Test
public void concurrentExecute() throws InterruptedException {
assertEquals(0, counter);
for(int i=0; i<1000; i++)
getConcurrentExecutorThread().start();
// wait for threads to complete
Thread.sleep(1000);
assertEquals(1, methodExecutor.mutexMap.size());
try
{
final List<long[]> infiniteList = new LinkedList<long[]>();
for(long i = Long.MIN_VALUE; i < Long.MAX_VALUE; i++)
infiniteList.add(new long[102400]);
fail("An OutOfMemoryError should be thrown");
}
catch(OutOfMemoryError e)
{
}
assertEquals(2000, counter);
assertEquals(0, methodExecutor.mutexMap.size());
}
// synchronous method
private ISynchronousMethod method = new ISynchronousMethod() {
#Override
public Object execute() {
counter++;
return null;
}
};
/**
* Executes a line of code.
*
* #return Thread
*/
private Thread getConcurrentExecutorThread() {
return new Thread() {
#Override
public void run() {
methodExecutor.doSynchronousMethod("TEST", "1", method);
try
{
Thread.sleep(500);
}
catch (InterruptedException e)
{
}
methodExecutor.doSynchronousMethod("TEST", new String("1"), method);
}
};
}
}
This last assertion is what breaks the test:
assertEquals(0, methodExecutor.mutexMap.size());
You're storing the exact same String object as both the key and the value. The key is a strong reference to the object, and as long as a strong reference to it exists, the weak reference to it is meaningless. The definition of weakly reachable (here) states that:
An object is weakly reachable if it is neither strongly nor softly reachable but can be reached by traversing a weak reference.
By the way, even with this corrected I don't think you can assume that the map will always be empty at the end. It will probably be close to empty, but I think that's all that can be said about it.
Weak references will only be collected when the JVM absolutely needs more memory.
Related
I have a method, disconnectUser() which among other things, assigns null to an object userSession at the end of execution. However, a hypothetical arose as I was coming up with the logic, where say userSession has a running method and it is assigned a null reference while it is still executing; how does the JVM deal with such a situation?
FWIW here are some code snippets for context:
public class A {
UserSession userSession;
/* Skipped irrelevant code
*-------------------------------
*/
private void disconnectUser(){
//Runs an endless while-loop (it's for demonstration's sake)
userSession.runEndlessLoop();
userSession = null;
}
}
ADDENDUM:
Here's the implementation for runEndlessLoop
public void runEndlessLoop(){
Executors.newSingleThreadExecutor().execute(() -> while(true){});
}
userSession is a reference to an object. You "assign" null to this reference, not to an object. So you are changing this reference. It doesn't change the object userSession formerly referenced / pointed to.
Also see: Can unreferenced objects still run if the garbage collector hasn't deleted them?
Let me try to add this: If the method of this object is running in the same thread as the rest of your program, the reference would be changed after this method finishes, so the problem doesn't even come up.
If, in contrast, this object acts in a different thread, well... i just tested it:
public class UnreferencedTest {
public static void main(String[] args) {
UnreferencedTest u = new UnreferencedTest();
u.createObject();
}
private void createObject() {
Unreferenced obj = new Unreferenced();
Thread t = new Thread(obj);//create new thread
t.start();
obj = null; //remove only reference to object
System.gc(); //ask GC to clean up
try {
Thread.sleep(10000); //wait a bit longer than other thread
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private class Unreferenced implements Runnable {
#Override
public void run() {
try {
Thread.sleep(5000);
areYouStillHere();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void areYouStillHere() {
System.out.println("I'm still here!");
}
}
}
... and even "asked" the GC to clean up unreferenced objects. (no guarantee it does!) It's only waiting for 5 seconds, but still runs.
Hope that helps!
Javadoc 8 for PhantomReference
states:
Phantom references are most often used for scheduling pre-mortem cleanup actions in a more flexible way than is possible with the Java finalization mechanism.
So I tried creating a thread that is calling the close() method of a Test Object that is eligible for garbage collection. The run() tries to get all Test Objects pre-mortem.
Actually the retrieved Test Objects are all null. The expected behavior is, that the Test Objects are retrieved and the closemethod is called.
No matter how many Test Objects you create there is not a single Test Object that could be caught pre-mortem (You have to increase the timeouts and call GC multiple times).
What am I doing wrong? Is this a Java Bug?
Runnable Test Code:
I tried to create a Minimal, Complete, and Verifiable example, but it's still quite long. I use java version "1.8.0_121" 32-bit on Windows 7 64-bit.
public class TestPhantomReference {
public static void main(String[] args) throws InterruptedException {
// Create AutoClose Thread and start it
AutoCloseThread thread = new AutoCloseThread();
thread.start();
// Add 10 Test Objects to the AutoClose Thread
// Test Objects are directly eligible for GC
for (int i = 0; i < 2; i++) {
thread.addObject(new Test());
}
// Sleep 1 Second, run GC, sleep 1 Second, interrupt AutoCLose Thread
Thread.sleep(1000);
System.out.println("System.gc()");
System.gc();
Thread.sleep(1000);
thread.interrupt();
}
public static class Test {
public void close() {
System.out.println("close()");
}
}
public static class AutoCloseThread extends Thread {
private ReferenceQueue<Test> mReferenceQueue = new ReferenceQueue<>();
private Stack<PhantomReference<Test>> mPhantomStack = new Stack<>();
public void addObject(Test pTest) {
// Create PhantomReference for Test Object with Reference Queue, add Reference to Stack
mPhantomStack.push(new PhantomReference<Test>(pTest, mReferenceQueue));
}
#Override
public void run() {
try {
while (true) {
// Get PhantomReference from ReferenceQueue and get the Test Object inside
Test testObj = mReferenceQueue.remove().get();
if (null != testObj) {
System.out.println("Test Obj call close()");
testObj.close();
} else {
System.out.println("Test Obj is null");
}
}
} catch (InterruptedException e) {
System.out.println("Thread Interrupted");
}
}
}
}
Expected Output:
System.gc()
Test Obj call close()
close()
Test Obj call close()
close()
Thread Interrupted
Actual Output:
System.gc()
Test Obj is null
Test Obj is null
Thread Interrupted
This is by design. Unlike finalize(), which makes an object reachable again, objects referable by a Reference object only can not be made reachable again. So when you are going to manage a resource through it, you have to store the necessary information into another object. It’s not unusual, to use the Reference object itself for it.
Consider the following modifications to your test program:
public class TestPhantomReference {
public static void main(String[] args) throws InterruptedException {
// create two Test Objects without closing them
for (int i = 0; i < 2; i++) {
new Test(i);
}
// create two Test Objects with proper resource management
try(Test t2=new Test(2); Test t3=new Test(3)) {
System.out.println("using Test 2 and 3");
}
// Sleep 1 Second, run GC, sleep 1 Second
Thread.sleep(1000);
System.out.println("System.gc()");
System.gc();
Thread.sleep(1000);
}
static class TestResource extends PhantomReference<Test> {
private int id;
private TestResource(int id, Test referent, ReferenceQueue<Test> queue) {
super(referent, queue);
this.id = id;
}
private void close() {
System.out.println("closed "+id);
}
}
public static class Test implements AutoCloseable {
static AutoCloseThread thread = new AutoCloseThread();
static { thread.start(); }
private final TestResource resource;
Test(int id) {
resource = thread.addObject(this, id);
}
public void close() {
resource.close();
thread.remove(resource);
}
}
public static class AutoCloseThread extends Thread {
private ReferenceQueue<Test> mReferenceQueue = new ReferenceQueue<>();
private Set<TestResource> mPhantomStack = new HashSet<>();
public AutoCloseThread() {
setDaemon(true);
}
TestResource addObject(Test pTest, int id) {
final TestResource rs = new TestResource(id, pTest, mReferenceQueue);
mPhantomStack.add(rs);
return rs;
}
void remove(TestResource rs) {
mPhantomStack.remove(rs);
}
#Override
public void run() {
try {
while (true) {
TestResource rs = (TestResource)mReferenceQueue.remove();
System.out.println(rs.id+" not properly closed, doing it now");
mPhantomStack.remove(rs);
rs.close();
}
} catch (InterruptedException e) {
System.out.println("Thread Interrupted");
}
}
}
}
which will print:
using Test 2 and 3
closed 3
closed 2
System.gc()
0 not properly closed, doing it now
closed 0
1 not properly closed, doing it now
closed 1
showing how using the correct idiom ensures that resources are closed timely and, unlike finalize(), the object can opt out the post-mortem cleanup which makes using the correct idiom even more efficient, as in that case, no additional GC cycle is needed to reclaim the object after finalization.
get() method on phantom references always return null.
At the moment phantom reference is enqueued object it was referencing is already collected by GC. You need to store data required to clean up in separate object (e.g. you can subclass PhantomReference).
Here you can find example code and more elaborate description about using PhantomReferences.
Unlike finalizer, phantom reference cannot resurrect unreachable object. This is its main advantage, though cost is more complicated supporting code.
I have a public method inside which I am using executor framework to create multiple threads and all threads are accessing the same private method.
Code below:
public String getValue(Map map1, Map map2, Map map3, Map map4) {
ExecutorService es = Executors.newFixedThreadPool(4);
CompletionService<Object> cs = new ExecutorCompletionService<Object>(es);
cs.submit(new Callable<Object>() {
public Object call() throws Exception {
getCount(map1);
return null;
}
});
cs.submit(new Callable<Object>() {
public Object call() throws Exception {
getCount(map2);
return null;
}
});
cs.submit(new Callable<Object>() {
public Object call() throws Exception {
getCount(map2);
return null;
}
});
cs.submit(new Callable<Object>() {
public Object call() throws Exception {
getCount(map3);
return null;
}
});
for (int i = 0; i < 4; i++) {
try {
cs.take().get(); // find the first completed task
} catch (InterruptedException e) {
} catch (ExecutionException e) {
}
}
}
private int getCount(final Map map) {
List<String> list = new ArrayList<>();
for (Map.Entry<> elem : map.entrySet()) {
list.add(elem .getValue()) ;
}
int count = 0;
for(String fields: list){
count + = // Some time consuming DB operation
}
return count;
}
Will there be any mixing up of value of variable declared in private method or any Hashmap iteration problem?
How the private method gets invoked? Will there be several copies of private method for different thread or all threads will execute the private method simultaneously.
Please bear with me if you find the question a little dumb :)
If you are asking about method argument and method internal variables the answer is no. These variables are allocated on stack and are different for each thread.
EDIT:
Issues may happen when passing shared (among several threads) not thread safe objects. For example your method foo() accepts parameter of type MyClass:
foo(MyClass param) {}
Somewhere into foo() you call param.bar() that operates (directly or indirectly) with not thread safe member variable. In this case you will get race condition.
(thanks to #lexicore)
The threads synchronization issues are relevant however for class fields.
There shouldn't be any problems with your code since you don't manipulate the parameters (e.g. adding/removing stuff from the maps ).
of course there is this assumption that those maps are not related (e.g sharing same resources) AND no where else in you program you will manipulate those objects at the same time of this method is running.
Yes my explanation may give the readers some headache. but out of experience working with map and multi-threaded programming, there could be tons of other stuff that could go wrong.
So a word of advice try to make your program as thread-safe as possible, even though you are very sure that nothing would go wrong.
Here is my code
public class FinalizableObject {
#Override
protected void finalize() throws Throwable {
System.out.println("finalize() invoked for " + this);
super.finalize();
}
}
public class Main {
private static void test() throws InterruptedException {
ReferenceQueue<FinalizableObject> rq = new ReferenceQueue<FinalizableObject>();
FinalizableObject obj = new FinalizableObject();
PhantomReference<FinalizableObject> pr1 = new PhantomReference<FinalizableObject>(obj, rq);
obj = null;
System.gc();
Reference<? extends Object> ref = rq.remove();
System.out.print("remove " + ref + " from reference queue\n");
}
public static void main(String[] args) {
try {
test();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
It's very strange, rq.remove() will be blocked forever. Why my finalizable object's phantom reference can not be put into reference queue? Has it been GC collected?
The problem is with your non-trivial finalize() method. In default implementation (in class Object) this method is actually empty. When its implementation isn't empty, then it is not guaranteed that object will be instantly collected after invoking finalize().
If you will modify your program in such style:
for (int i = 0; i < 1000; i++)
System.gc();
i.e. you will call GC more then one times - it could lead to the rq object be fully collected (test on my machine).
Also, I suggest you following links to explore:
Java: PhantomReference, ReferenceQueue and finalize
Discovering Objects with Non-trivial Finalizers
The Secret Life Of The Finalizer: page 2 of 2
UPD: Another way: you have to hold in mind, that non-trivial finalize() methods is invoked by special Finalizer-Thread, which also have to be collected. So, for full pr collecting you can do such things:
a) create flag inside Main method:
public static volatile boolean flag;
b) set flag in finalize() method:
#Override
protected void finalize() throws Throwable {
System.out.println("finalize() invoked for " + this);
super.finalize();
Main.flag = true;
}
c) check flag for true and then call gc() again:
System.gc();
while (!flag) Thread.sleep(10);
System.gc();
In my application I'm performing somewhat heavy lookup operations. These operations must be done within a single thread (persistence framework limitation).
I want to cache the results. Thus, I have a class UMRCache, with an inner class Worker:
public class UMRCache {
private Worker worker;
private List<String> requests = Collections.synchronizedList<new ArrayList<String>>());
private Map<String, Object> cache = Collections.synchronizedMap(new HashMap<String, Object>());
public UMRCache(Repository repository) {
this.worker = new Worker(repository);
this.worker.start();
}
public Object get(String key) {
if (this.cache.containsKey(key)) {
// If the element is already cached, get value from cache
return this.cache.get(key);
}
synchronized (this.requests) {
// Add request to queue
this.requests.add(key);
// Notify the Worker thread that there's work to do
this.requests.notifyAll();
}
synchronized (this.cache) {
// Wait until Worker has updated the cache
this.cache.wait();
// Now, cache should contain a value for key
return this.cache.get(key);
}
}
private class Worker extends Thread {
public void run() {
boolean doRun = true;
while (doRun) {
synchronized (requests) {
while (requests.isEmpty() && doRun) {
requests.wait(); // Wait until there's work to do
}
synchronized (cache) {
Set<String> processed = new HashSet<String>();
for (String key : requests) {
// Do the lookup
Object result = respository.lookup(key);
// Save to cache
cache.put(key, result);
processed.add(key);
}
// Remove processed requests from queue
requests.removeAll(processed);
// Notify all threads waiting for their requests to be served
cache.notifyAll();
}
}
}
}
}
}
I have a testcase for this:
public class UMRCacheTest extends TestCase {
private UMRCache umrCache;
public void setUp() throws Exception {
super.setUp();
umrCache = new UMRCache(repository);
}
public void testGet() throws Exception {
for (int i = 0; i < 10000; i++) {
final List fetched = Collections.synchronizedList(new ArrayList());
final String[] keys = new String[]{"key1", "key2"};
final String[] expected = new String[]{"result1", "result2"}
final Random random = new Random();
Runnable run1 = new Runnable() {
public void run() {
for (int i = 0; i < keys.length; i++) {
final String key = keys[i];
final Object result = umrCache.get(key);
assertEquals(key, results[i]);
fetched.add(um);
try {
Thread.sleep(random.nextInt(3));
} catch (InterruptedException ignore) {
}
}
}
};
Runnable run2 = new Runnable() {
public void run() {
for (int i = keys.length - 1; i >= 0; i--) {
final String key = keys[i];
final String result = umrCache.get(key);
assertEquals(key, results[i]);
fetched.add(um);
try {
Thread.sleep(random.nextInt(3));
} catch (InterruptedException ignore) {
}
}
}
};
final Thread thread1 = new Thread(run1);
thread1.start();
final Thread thread2 = new Thread(run2);
thread2.start();
final Thread thread3 = new Thread(run1);
thread3.start();
thread1.join();
thread2.join();
thread3.join();
umrCache.dispose();
assertEquals(6, fetched.size());
}
}
}
The test fails randomly, at about 1 out of 10 runs. It will fail at the last assertion: assertEquals(6, fetched.size()), at assertEquals(key, results[i]), or sometimes the test runner will never finish.
So there's something buggy about my thread logic. Any tips?
EDIT:
I might have cracked it now, thanks to all who have helped.
The solution seems to be:
public Object get(String key) {
if (this.cache.containsKey(key)) {
// If the element is already cached, get value from cache
return this.cache.get(key);
}
synchronized (this.requests) {
// Add request to queue
this.requests.add(key);
// Notify the Worker thread that there's work to do
this.requests.notifyAll();
}
synchronized (this.cache) {
// Wait until Worker has updated the cache
while (!this.cache.containsKey(key)) {
this.cache.wait();
}
// Now, cache should contain a value for key
return this.cache.get(key);
}
}
get() method logic can miss result and get stuck
synchronized (this.requests) {
// Add request to queue
this.requests.add(key);
// Notify the Worker thread that there's work to do
this.requests.notifyAll();
}
// ----- MOMENT1. If at this moment Worker puts result into cache it
// will be missed since notification will be lost
synchronized (this.cache) {
// Wait until Worker has updated the cache
this.cache.wait();
// ----- MOMENT2. May be too late, since cache notifiation happened before at MOMENT1
// Now, cache should contain a value for key
return this.cache.get(key);
}
The variable fetched in your test is an ArrayList and is accessed and updated from your two anonymous Runnable instances.
ArrayList is not thread safe, from the documentation:
Note that this implementation is not
synchronized. If multiple threads
access an ArrayList instance
concurrently, and at least one of the
threads modifies the list
structurally, it must be synchronized
externally. (A structural modification
is any operation that adds or deletes
one or more elements, or explicitly
resizes the backing array; merely
setting the value of an element is not
a structural modification.) This is
typically accomplished by
synchronizing on some object that
naturally encapsulates the list. If no
such object exists, the list should be
"wrapped" using the
Collections.synchronizedList method.
This is best done at creation time, to
prevent accidental unsynchronized
access to the list:
Hence I think your test needs a little adjusting.
I noticed your lookup in cache isn't atomic operation:
if (this.cache.containsKey(key)) {
// If the element is already cached, get value from cache
return this.cache.get(key);
}
Since you never delete from cache in your code, you always will get some value by this code. But if, in future, you plan to clean cache, lack of atomicity here will become a problem.