/* Copyright (c) 2007 Timothy Wall, All Rights Reserved
*
* The contents of this file is dual-licensed under 2
* alternative Open Source/Free licenses: LGPL 2.1 or later and
* Apache License 2.0. (starting with JNA version 4.0.0).
*
* You can freely decide which license you want to apply to
* the project.
*
* You may obtain a copy of the LGPL License at:
*
* http://www.gnu.org/licenses/licenses.html
*
* A copy is also included in the downloadable source code package
* containing JNA, in file "LGPL2.1".
*
* You may obtain a copy of the Apache License at:
*
* http://www.apache.org/licenses/
*
* A copy is also included in the downloadable source code package
* containing JNA, in file "AL2.0".
*/
package com.sun.jna;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.util.Map;
import junit.framework.TestCase;
public class MemoryTest extends TestCase implements GCWaits {
public void testAutoFreeMemory() throws Exception {
final boolean[] flag = { false };
Memory core = new Memory(10) {
@Override
protected void finalize() {
super.finalize();
flag[0] = true;
}
};
Pointer shared = core.share(0, 5);
Reference<Memory> ref = new WeakReference<Memory>(core);
core = null;
System.gc();
assertFalse("Memory prematurely GC'd", flag[0]);
assertNotNull("Base memory GC'd while shared memory extant", ref.get());
// Avoid having IBM J9 prematurely nullify "shared"
shared.setInt(0, 0);
shared = null;
long start = System.currentTimeMillis();
System.gc();
Memory.purge();
for (int i=0;i < GC_WAITS && ref.get() != null;i++) {
Thread.sleep(GC_WAIT_INTERVAL);
System.gc();
Memory.purge();
}
long end = System.currentTimeMillis();
assertNull("Memory not GC'd after " + (end - start) + " millis", ref.get());
}
public void testShareMemory() {
Memory base = new Memory(8);
Pointer shared = base.share(0);
assertNotSame("Memory share should return a different object", base, shared);
}
public void testSharedMemoryBounds() {
Memory base = new Memory(16);
Pointer shared = base.share(4, 4);
shared.getInt(-4);
try {
shared.getInt(-8);
fail("Bounds check should fail");
}
catch(IndexOutOfBoundsException e) {
}
shared.getInt(8);
try {
shared.getInt(12);
fail("Bounds check should fail");
}
catch(IndexOutOfBoundsException e) {
}
}
public void testAlignment() {
final int SIZE = 1<<16;
Memory base = new Memory(SIZE);
for (int align=1;align < SIZE;align *= 2) {
Memory unaligned = base;
long mask = ~((long)align - 1);
if ((base.peer & mask) == base.peer)
unaligned = (Memory)base.share(1, SIZE-1);
Pointer aligned = unaligned.align(align);
assertEquals("Memory not aligned (" + align + ")",
aligned.peer & mask, aligned.peer);
assertSame("Alignment request on aligned memory should no-op",
aligned, ((Memory)aligned).align(align));
}
}
public void testNegativeAlignment() {
final int SIZE = 128;
Memory base = new Memory(SIZE);
try {
base.align(-1);
fail("Negative alignments not allowed");
}
catch(IllegalArgumentException e) { }
}
public void testInvalidAlignment() {
final int SIZE = 128;
Memory base = new Memory(SIZE);
int[] alignments = { 0, 3, 5, 9, 13 };
for (int i=0;i < alignments.length;i++) {
try {
base.align(alignments[i]);
fail("Power-of-two alignments required");
}
catch(IllegalArgumentException e) { }
}
}
public void testAvoidGCWithExtantBuffer() throws Exception {
if (!Platform.HAS_BUFFERS) return;
Memory m = new Memory(1024);
m.clear();
ByteBuffer b = m.getByteBuffer(0, m.size());
Reference<Memory> ref = new WeakReference<Memory>(m);
Reference<ByteBuffer> bref = new WeakReference<ByteBuffer>(b);
// Create a second byte buffer "equal" to the first
m = new Memory(1024);
m.clear();
m.getByteBuffer(0, m.size());
m = null;
System.gc();
Memory.purge();
for (int i=0;i < GC_WAITS && ref.get() != null;i++) {
Thread.sleep(GC_WAIT_INTERVAL);
System.gc();
Memory.purge();
}
assertNotNull("Memory GC'd while NIO Buffer still exists", ref.get());
// Avoid IBM J9 optimization resulting in premature GC of buffer
b.put((byte)0);
b = null;
System.gc();
Memory.purge();
for (int i=0;i < GC_WAITS && (bref.get() != null || ref.get() != null);i++) {
Thread.sleep(GC_WAIT_INTERVAL);
System.gc();
Memory.purge();
}
assertNull("Buffer not GC'd\n", bref.get());
assertNull("Memory not GC'd after buffer GC'd\n", ref.get());
}
public void testDump() {
// test with 15 bytes so last line has less than 4 bytes
int n = 15;
Memory m = new Memory(n);
for (int i = 0; i < n; i++) {
m.setByte(i, (byte) i);
}
String ls = System.getProperty("line.separator");
assertEquals("memory dump" + ls +
"[00010203]" + ls +
"[04050607]" + ls +
"[08090a0b]" + ls +
"[0c0d0e]" + ls, m.dump());
}
public void testRemoveAllocatedMemoryMap() throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
// Make sure there are no remaining allocations
Memory.disposeAll();
// get a reference to the allocated memory
Field allocatedMemoryField = Memory.class.getDeclaredField("allocatedMemory");
allocatedMemoryField.setAccessible(true);
Map<Memory, Reference<Memory>> allocatedMemory = (Map<Memory, Reference<Memory>>) allocatedMemoryField.get(null);
assertEquals(0, allocatedMemory.size());
// Test allocation and ensure it is accounted for
Memory mem = new Memory(1024);
assertEquals(1, allocatedMemory.size());
// Dispose memory and ensure allocation is removed from allocatedMemory-Map
mem.dispose();
assertEquals(0, allocatedMemory.size());
}
public static void main(String[] args) {
junit.textui.TestRunner.run(MemoryTest.class);
}
}