You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
128 lines
4.8 KiB
128 lines
4.8 KiB
/*
|
|
* Copyright (C) 2013 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
import java.lang.Runtime;
|
|
import java.lang.ref.ReferenceQueue;
|
|
import java.lang.ref.PhantomReference;
|
|
import dalvik.system.VMRuntime;
|
|
|
|
public class Main {
|
|
static Object deadlockLock = new Object();
|
|
static VMRuntime runtime = VMRuntime.getRuntime();
|
|
static volatile boolean aboutToDeadlock = false;
|
|
static final long MAX_EXPECTED_GC_DURATION_MS = 2000;
|
|
|
|
// Save ref as a static field to ensure it doesn't get GC'd before the
|
|
// referent is enqueued.
|
|
static PhantomReference ref = null;
|
|
|
|
static class DeadlockingFinalizer {
|
|
protected void finalize() throws Exception {
|
|
aboutToDeadlock = true;
|
|
synchronized (deadlockLock) { }
|
|
}
|
|
}
|
|
|
|
private static void allocateDeadlockingFinalizer() {
|
|
new DeadlockingFinalizer();
|
|
}
|
|
|
|
public static PhantomReference allocPhantom(ReferenceQueue<Object> queue) {
|
|
return new PhantomReference(new Object(), queue);
|
|
}
|
|
|
|
// Test that calling registerNativeAllocation triggers a GC eventually
|
|
// after a substantial number of registered native bytes.
|
|
private static void checkRegisterNativeAllocation() throws Exception {
|
|
long maxMem = Runtime.getRuntime().maxMemory();
|
|
int size = (int)(maxMem / 32);
|
|
int allocationCount = 256;
|
|
|
|
ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
|
|
ref = allocPhantom(queue);
|
|
long total = 0;
|
|
for (int i = 0; !ref.isEnqueued() && i < allocationCount; ++i) {
|
|
runtime.registerNativeAllocation(size);
|
|
total += size;
|
|
|
|
// Sleep a little bit to ensure not all of the calls to
|
|
// registerNativeAllocation complete while GC is in the process of
|
|
// running.
|
|
Thread.sleep(MAX_EXPECTED_GC_DURATION_MS / allocationCount);
|
|
}
|
|
|
|
// Wait up to MAX_EXPECTED_GC_DURATION_MS to give GC a chance to finish
|
|
// running. If the reference isn't enqueued after that, then it is
|
|
// pretty unlikely (though technically still possible) that GC was
|
|
// triggered as intended.
|
|
if (queue.remove(MAX_EXPECTED_GC_DURATION_MS) == null) {
|
|
throw new RuntimeException("GC failed to complete");
|
|
}
|
|
|
|
while (total > 0) {
|
|
runtime.registerNativeFree(size);
|
|
total -= size;
|
|
}
|
|
}
|
|
|
|
// Call registerNativeAllocation repeatedly at a high rate to trigger the case of blocking
|
|
// registerNativeAllocation. Stop before we risk exhausting the finalizer timeout.
|
|
private static void triggerBlockingRegisterNativeAllocation() throws Exception {
|
|
final long startTime = System.currentTimeMillis();
|
|
final long finalizerTimeoutMs = VMRuntime.getRuntime().getFinalizerTimeoutMs();
|
|
final long quittingTime = startTime + finalizerTimeoutMs - MAX_EXPECTED_GC_DURATION_MS;
|
|
long maxMem = Runtime.getRuntime().maxMemory();
|
|
int size = (int)(maxMem / 5);
|
|
int allocationCount = 10;
|
|
|
|
long total = 0;
|
|
for (int i = 0; i < allocationCount && System.currentTimeMillis() < quittingTime; ++i) {
|
|
runtime.registerNativeAllocation(size);
|
|
total += size;
|
|
}
|
|
|
|
while (total > 0) {
|
|
runtime.registerNativeFree(size);
|
|
total -= size;
|
|
}
|
|
}
|
|
|
|
public static void main(String[] args) throws Exception {
|
|
// Test that registerNativeAllocation triggers GC.
|
|
// Run this a few times in a loop to reduce the chances that the test
|
|
// is flaky and make sure registerNativeAllocation continues to work
|
|
// after the first GC is triggered.
|
|
for (int i = 0; i < 20; ++i) {
|
|
checkRegisterNativeAllocation();
|
|
}
|
|
|
|
// Test that we don't get a deadlock if we call
|
|
// registerNativeAllocation with a blocked finalizer.
|
|
synchronized (deadlockLock) {
|
|
allocateDeadlockingFinalizer();
|
|
while (!aboutToDeadlock) {
|
|
Runtime.getRuntime().gc();
|
|
}
|
|
|
|
// Do more allocations now that the finalizer thread is deadlocked so that we force
|
|
// finalization and timeout.
|
|
triggerBlockingRegisterNativeAllocation();
|
|
}
|
|
System.out.println("Test complete");
|
|
}
|
|
}
|
|
|