This is the mail archive of the
java-patches@sourceware.cygnus.com
mailing list for the Java project.
Patch: Bitmap descriptor GC marking
- To: java-patches at sourceware dot cygnus dot com
- Subject: Patch: Bitmap descriptor GC marking
- From: Bryce McKinlay <bryce at albatross dot co dot nz>
- Date: Sun, 07 May 2000 21:14:14 +1200
- CC: "Boehm, Hans" <hans_boehm at hp dot com>
Here's my patch that tries to implement bitmap based GC marking. I'm
posting it in the hope that someone who knows more about the collector
than I do can figure out why it doesnt work right ;-)
The problems, as far as I can tell, are that if GC_GCJ_MALLOC() is used:
- the heap in some cases grows much bigger than usual when using arrays.
- finialization doesn't seem to work at all.
I've also attached a "GCTest" which shows the problems. A "normal"
GCTest run should look something like this:
$ ./gctest
smallArrays() : .......... 6048ms (heap: total=18276K free=6392K)
bigArrays() : .......... 8455ms (heap: total=20324K free=10428K)
objArray() : .......... 8471ms (heap: total=22372K free=7532K)
finalization(): .......... [900000] 9522ms (heap: total=22372K
free=5148K)
but a run using this patch looks like this:
$ ./gctest
smallArrays() : .......... 8655ms (heap: total=18276K free=6396K)
bigArrays() : .......... 46094ms (heap: total=63420K free=17804K)
objArray() : .......... 11250ms (heap: total=63420K free=44388K)
finalization(): .......... [0] 3149ms (heap: total=63420K free=53284K)
(the second run is slower because its a debug build. Note the heap
growth and the lack of finalization.)
I've been kicking this patch around for a while - earlier versions also
did things like change the array and vtable allocations and inline the
array allocation functions. This is an attempt to create a minimal patch
to try and isolate the problems before moving on.
regards
[ bryce ]
2000-05-07 Bryce McKinlay <bryce@albatross.co.nz>
* Makefile.am: Build gcj_mlc.c.
* Makefile.in: Updated.
* configure.in: Define GC_GCJ_SUPPORT.
* configure: Updated.
2000-05-07 Bryce McKinlay <bryce@albatross.co.nz>
* configure.in: Define JC1GCSPEC. Set it if boehm-gc is used.
* configure: Updated.
* libgcj.spec.in: Pass JC1GCSPEC to jc1.
* boehm.cc: Include gc_gcj.h.
(_Jv_AllocObj): New parameter `klass'. Use GC_GCJ_MALLOC ().
(_Jv_AllocArray): New parameter `klass'. Set vtable pointer of new
object before returning.
(_Jv_InitGC): Call GC_init_gcj_malloc (). Don't set up marking and
allocation for obj_kind_x.
* prims.cc (_Jv_AllocObject): Rename parameter `c' to `klass'. Pass
`klass' to _Jv_AllocObj. Don't set the new object's vtable.
(_Jv_NewObjectArray): Rename parameter `clas' to `klass'. Pass
`klass' to _Jv_AllocArray. Don't set the new array's vtable.
(_Jv_NewPrimArray): Call _Jv_FindArrayClass before _Jv_AllocObj. Pass
`klass' to _Jv_AllocObj. Don't set the new array's vtable.
* include/jvm.h (struct _Jv_VTable): New field `gc_descr'.
(_Jv_AllocObj): Declare new jclass parameter.
(_Jv_AllocArray): Ditto.
* java/lang/Class.h (_Jv_NewObjectArray): No longer a friend.
(_Jv_NewPrimArray): Ditto.
(_Jv_AllocObj): Declare as a friend.
(_Jv_AllocArray): Ditto.
* java/lang/natClassLoader.cc (_Jv_FindArrayClass): Copy marking
descriptor from &ObjectClass into new array class.
Index: boehm-gc/Makefile.am
===================================================================
RCS file: /cvs/java/libgcj/boehm-gc/Makefile.am,v
retrieving revision 1.15
diff -u -r1.15 Makefile.am
--- Makefile.am 2000/04/19 12:47:53 1.15
+++ Makefile.am 2000/05/07 08:44:48
@@ -27,7 +27,7 @@
EXTRA_LTLIBRARIES = libgcjgc.la
libgcjgc_la_SOURCES = allchblk.c alloc.c blacklst.c checksums.c \
gcconfig.h dbg_mlc.c dyn_load.c finalize.c gc.h gc_alloc.h gc_cpp.h \
-gc_hdrs.h gc_mark.h gc_priv.h gc_private.h gc_typed.h headers.c \
+gc_hdrs.h gc_mark.h gc_priv.h gc_private.h gc_typed.h gcj_mlc.c headers.c \
hpux_irix_threads.c linux_threads.c malloc.c mallocx.c mark.c mark_rts.c \
misc.c new_hblk.c obj_map.c os_dep.c pcr_interface.c ptr_chck.c \
real_malloc.c reclaim.c solaris_pthreads.c solaris_threads.c \
Index: boehm-gc/Makefile.in
===================================================================
RCS file: /cvs/java/libgcj/boehm-gc/Makefile.in,v
retrieving revision 1.18
diff -u -r1.18 Makefile.in
--- Makefile.in 2000/04/27 00:43:33 1.18
+++ Makefile.in 2000/05/07 08:44:50
@@ -108,7 +108,7 @@
EXTRA_LTLIBRARIES = libgcjgc.la
libgcjgc_la_SOURCES = allchblk.c alloc.c blacklst.c checksums.c \
gcconfig.h dbg_mlc.c dyn_load.c finalize.c gc.h gc_alloc.h gc_cpp.h \
-gc_hdrs.h gc_mark.h gc_priv.h gc_private.h gc_typed.h headers.c \
+gc_hdrs.h gc_mark.h gc_priv.h gc_private.h gc_typed.h gcj_mlc.c headers.c \
hpux_irix_threads.c linux_threads.c malloc.c mallocx.c mark.c mark_rts.c \
misc.c new_hblk.c obj_map.c os_dep.c pcr_interface.c ptr_chck.c \
real_malloc.c reclaim.c solaris_pthreads.c solaris_threads.c \
@@ -197,7 +197,7 @@
LDFLAGS = @LDFLAGS@
LIBS = @LIBS@
libgcjgc_la_OBJECTS = allchblk.lo alloc.lo blacklst.lo checksums.lo \
-dbg_mlc.lo dyn_load.lo finalize.lo headers.lo hpux_irix_threads.lo \
+dbg_mlc.lo dyn_load.lo finalize.lo gcj_mlc.lo headers.lo hpux_irix_threads.lo \
linux_threads.lo malloc.lo mallocx.lo mark.lo mark_rts.lo misc.lo \
new_hblk.lo obj_map.lo os_dep.lo pcr_interface.lo ptr_chck.lo \
real_malloc.lo reclaim.lo solaris_pthreads.lo solaris_threads.lo \
Index: boehm-gc/configure
===================================================================
RCS file: /cvs/java/libgcj/boehm-gc/configure,v
retrieving revision 1.21
diff -u -r1.21 configure
--- configure 2000/04/27 00:43:33 1.21
+++ configure 2000/05/07 08:45:54
@@ -2258,6 +2258,9 @@
#define JAVA_FINALIZATION 1
EOF
+cat >> confdefs.h <<\EOF
+#define GC_GCJ_SUPPORT 1
+EOF
if test -n "${with_cross_host}"; then
cat >> confdefs.h <<\EOF
Index: boehm-gc/configure.in
===================================================================
RCS file: /cvs/java/libgcj/boehm-gc/configure.in,v
retrieving revision 1.17
diff -u -r1.17 configure.in
--- configure.in 2000/04/19 10:09:57 1.17
+++ configure.in 2000/05/07 08:45:54
@@ -191,6 +191,7 @@
AC_DEFINE(NO_SIGNALS)
AC_DEFINE(NO_DEBUGGING)
AC_DEFINE(JAVA_FINALIZATION)
+AC_DEFINE(GC_GCJ_SUPPORT)
dnl This is something of a hack. When cross-compiling we turn off
dnl some functionality. We also enable the "small" configuration.
Index: libjava/boehm.cc
===================================================================
RCS file: /cvs/java/libgcj/libjava/boehm.cc,v
retrieving revision 1.20
diff -u -r1.20 boehm.cc
--- boehm.cc 2000/05/06 23:32:52 1.20
+++ boehm.cc 2000/05/07 08:45:54
@@ -28,6 +28,7 @@
{
#include <gc_priv.h>
#include <gc_mark.h>
+#include <include/gc_gcj.h>
// These aren't declared in any Boehm GC header.
void GC_finalize_all (void);
@@ -295,22 +296,21 @@
return mark_stack_ptr;
}
-// Allocate space for a new Java object. FIXME: this might be the
-// wrong interface; we might prefer to pass in the object type as
-// well. It isn't important for this collector, but it might be for
-// other collectors.
+// Allocate space for a new Java object.
void *
-_Jv_AllocObj (jsize size)
+_Jv_AllocObj (jsize size, jclass klass)
{
- return GC_GENERIC_MALLOC (size, obj_kind_x);
+ return GC_GCJ_MALLOC (size, klass->vtable);
}
-// Allocate space for a new Java array. FIXME: again, this might be
-// the wrong interface.
+// Allocate space for a new Java array.
void *
-_Jv_AllocArray (jsize size)
+_Jv_AllocArray (jsize size, jclass klass)
{
- return GC_GENERIC_MALLOC (size, array_kind_x);
+ ptr_t obj = GC_GENERIC_MALLOC (size, array_kind_x);
+ *((_Jv_VTable **) obj) = klass->vtable;
+ return obj;
}
// Allocate some space that is known to be pointer-free.
@@ -423,7 +423,57 @@
return;
}
initialized = 1;
+ UNLOCK ();
+
+ // Configure the collector to use the bitmap marking descriptors that we
+ // stash in the class vtable.
+ GC_init_gcj_malloc (0, (void *) _Jv_MarkObj);
+
+ LOCK ();
+ GC_java_finalization = 1;
+
+ // We use a different mark procedure for object arrays. This code
+ // configures a different object `kind' for object array allocation and
+ // marking.
+ array_free_list = (ptr_t *) GC_generic_malloc_inner ((MAXOBJSZ + 1)
+ * sizeof (ptr_t),
+ PTRFREE);
+ memset (array_free_list, 0, (MAXOBJSZ + 1) * sizeof (ptr_t));
+
+ proc = GC_n_mark_procs++;
+ GC_mark_procs[proc] = (mark_proc) _Jv_MarkArray;
+
+ array_kind_x = GC_n_kinds++;
+ GC_obj_kinds[array_kind_x].ok_freelist = array_free_list;
+ GC_obj_kinds[array_kind_x].ok_reclaim_list = 0;
+ GC_obj_kinds[array_kind_x].ok_descriptor = MAKE_PROC (proc, 0);
+ GC_obj_kinds[array_kind_x].ok_relocate_descr = FALSE;
+ GC_obj_kinds[array_kind_x].ok_init = TRUE;
+
+ _Jv_MutexInit (&disable_gc_mutex);
+ UNLOCK ();
+ ENABLE_SIGNALS ();
+}
+
+#if 0
+void
+_Jv_InitGC (void)
+{
+ int proc;
+ DCL_LOCK_STATE;
+
+ DISABLE_SIGNALS ();
+ LOCK ();
+
+ if (initialized)
+ {
+ UNLOCK ();
+ ENABLE_SIGNALS ();
+ return;
+ }
+ initialized = 1;
+
GC_java_finalization = 1;
// Set up state for marking and allocation of Java objects.
@@ -464,3 +514,4 @@
UNLOCK ();
ENABLE_SIGNALS ();
}
+#endif
Index: libjava/configure
===================================================================
RCS file: /cvs/java/libgcj/libjava/configure,v
retrieving revision 1.58
diff -u -r1.58 configure
--- configure 2000/04/21 20:46:10 1.58
+++ configure 2000/05/07 08:46:17
@@ -2467,6 +2467,7 @@
GCDEPS=
GCOBJS=
GCSPEC=
+JC1GCSPEC=
GCTESTSPEC=
case "$GC" in
boehm)
@@ -2477,6 +2478,7 @@
GCLIBS="$GCDEPS -L\$(here)/../boehm-gc/$libsubdir"
GCINCS='-I$(top_srcdir)/../boehm-gc -I$(top_builddir)/../boehm-gc'
GCSPEC='-lgcjgc'
+ JC1GCSPEC='-fuse-boehm-gc'
GCTESTSPEC="-L`pwd`/../boehm-gc/.libs -rpath `pwd`/../boehm-gc/.libs"
GCINCS="$GCINCS `cat ../boehm-gc/boehm-cflags`"
GCOBJS=boehm.lo
@@ -5339,6 +5341,7 @@
s%@GCDEPS@%$GCDEPS%g
s%@GCOBJS@%$GCOBJS%g
s%@GCSPEC@%$GCSPEC%g
+s%@JC1GCSPEC@%$JC1GCSPEC%g
s%@GCTESTSPEC@%$GCTESTSPEC%g
s%@THREADLIBS@%$THREADLIBS%g
s%@THREADINCS@%$THREADINCS%g
Index: libjava/configure.in
===================================================================
RCS file: /cvs/java/libgcj/libjava/configure.in,v
retrieving revision 1.57
diff -u -r1.57 configure.in
--- configure.in 2000/04/21 20:46:11 1.57
+++ configure.in 2000/05/07 08:46:20
@@ -220,6 +220,7 @@
GCDEPS=
GCOBJS=
GCSPEC=
+JC1GCSPEC=
GCTESTSPEC=
case "$GC" in
boehm)
@@ -230,6 +231,7 @@
GCLIBS="$GCDEPS -L\$(here)/../boehm-gc/$libsubdir"
GCINCS='-I$(top_srcdir)/../boehm-gc -I$(top_builddir)/../boehm-gc'
GCSPEC='-lgcjgc'
+ JC1GCSPEC='-fuse-boehm-gc'
GCTESTSPEC="-L`pwd`/../boehm-gc/.libs -rpath `pwd`/../boehm-gc/.libs"
dnl We also want to pick up some cpp flags required when including
dnl boehm-config.h. Yuck.
Index: libjava/libgcj.spec.in
===================================================================
RCS file: /cvs/java/libgcj/libjava/libgcj.spec.in,v
retrieving revision 1.9
diff -u -r1.9 libgcj.spec.in
--- libgcj.spec.in 2000/04/28 16:11:56 1.9
+++ libgcj.spec.in 2000/05/07 08:46:20
@@ -6,7 +6,7 @@
%rename lib liborig
*lib: -lgcj -lm @GCSPEC@ @THREADSPEC@ @ZLIBSPEC@ @SYSTEMSPEC@ %(liborig)
-*jc1: @DIVIDESPEC@ @EXCEPTIONSPEC@ -fasynchronous-exceptions
+*jc1: @DIVIDESPEC@ @EXCEPTIONSPEC@ @JC1GCSPEC@ -fasynchronous-exceptions
#
# libgcc should really be a shared library. This is a design flaw
Index: libjava/prims.cc
===================================================================
RCS file: /cvs/java/libgcj/libjava/prims.cc,v
retrieving revision 1.29
diff -u -r1.29 prims.cc
--- prims.cc 2000/05/05 02:56:14 1.29
+++ prims.cc 2000/05/07 08:46:26
@@ -319,18 +319,17 @@
return r;
}
-// Allocate a new object of class C. SIZE is the size of the object
+// Allocate a new object of class KLASS. SIZE is the size of the object
// to allocate. You might think this is redundant, but it isn't; some
// classes, such as String, aren't of fixed size.
jobject
-_Jv_AllocObject (jclass c, jint size)
+_Jv_AllocObject (jclass klass, jint size)
{
- _Jv_InitClass (c);
+ _Jv_InitClass (klass);
- jobject obj = (jobject) _Jv_AllocObj (size);
+ jobject obj = (jobject) _Jv_AllocObj (size, klass);
if (__builtin_expect (! obj, false))
JvThrow (no_memory);
- *((_Jv_VTable **) obj) = c->vtable;
// If this class has inherited finalize from Object, then don't
// bother registering a finalizer. We know that finalize() is the
@@ -339,7 +338,7 @@
// implementation would look for Object.finalize in Object's method
// table at startup, and then use that information to find the
// appropriate index in the method vector.
- if (c->vtable->method[1] != ObjectClass.vtable->method[1])
+ if (klass->vtable->method[1] != ObjectClass.vtable->method[1])
_Jv_RegisterFinalizer (obj, _Jv_FinalizeObject);
#ifdef ENABLE_JVMPI
@@ -352,7 +351,7 @@
event.event_type = JVMPI_EVENT_OBJECT_ALLOC;
event.env_id = NULL;
event.u.obj_alloc.arena_id = 0;
- event.u.obj_alloc.class_id = (jobjectID) c;
+ event.u.obj_alloc.class_id = (jobjectID) klass;
event.u.obj_alloc.is_array = 0;
event.u.obj_alloc.size = size;
event.u.obj_alloc.obj_id = (jobjectID) obj;
@@ -389,9 +388,9 @@
size += count * sizeof (jobject);
// FIXME: second argument should be "current loader" //
- jclass clas = _Jv_FindArrayClass (elementClass, 0);
+ jclass klass = _Jv_FindArrayClass (elementClass, 0);
- obj = (jobjectArray) _Jv_AllocArray (size);
+ obj = (jobjectArray) _Jv_AllocArray (size, klass);
if (__builtin_expect (! obj, false))
JvThrow (no_memory);
obj->length = count;
@@ -403,10 +402,6 @@
while (--count >= 0)
*ptr++ = init;
}
- // Set the vtbl last to avoid problems if the GC happens during the
- // window in this function between the allocation and this
- // assignment.
- *((_Jv_VTable **) obj) = clas->vtable;
return obj;
}
@@ -428,17 +423,14 @@
(SIZE_T_MAX - size) / elsize, false))
JvThrow (no_memory);
- __JArray *arr = (__JArray*) _Jv_AllocObj (size + elsize * count);
+ jclass klass = _Jv_FindArrayClass (eltype, 0);
+
+ __JArray *arr = (__JArray*) _Jv_AllocObj (size + elsize * count, klass);
if (__builtin_expect (! arr, false))
JvThrow (no_memory);
arr->length = count;
// Note that we assume we are given zeroed memory by the allocator.
- jclass klass = _Jv_FindArrayClass (eltype, 0);
- // Set the vtbl last to avoid problems if the GC happens during the
- // window in this function between the allocation and this
- // assignment.
- *((_Jv_VTable **) arr) = klass->vtable;
return arr;
}
Index: libjava/include/jvm.h
===================================================================
RCS file: /cvs/java/libgcj/libjava/include/jvm.h,v
retrieving revision 1.24
diff -u -r1.24 jvm.h
--- jvm.h 2000/04/27 23:13:31 1.24
+++ jvm.h 2000/05/07 08:46:26
@@ -31,7 +31,8 @@
struct _Jv_VTable
{
jclass clas;
- void *method[1];
+ void *gc_descr;
+ void *method[];
};
union _Jv_word
@@ -94,9 +95,9 @@
typedef void _Jv_FinalizerFunc (jobject);
/* Allocate space for a new Java object. */
-void *_Jv_AllocObj (jsize size) __attribute__((__malloc__));
+void *_Jv_AllocObj (jsize size, jclass cl) __attribute__((__malloc__));
/* Allocate space for an array of Java objects. */
-void *_Jv_AllocArray (jsize size) __attribute__((__malloc__));
+void *_Jv_AllocArray (jsize size, jclass cl) __attribute__((__malloc__));
/* Allocate space that is known to be pointer-free. */
void *_Jv_AllocBytes (jsize size) __attribute__((__malloc__));
/* Initialize the GC. */
Index: libjava/java/lang/Class.h
===================================================================
RCS file: /cvs/java/libgcj/libjava/java/lang/Class.h,v
retrieving revision 1.20
diff -u -r1.20 Class.h
--- Class.h 2000/05/05 02:56:14 1.20
+++ Class.h 2000/05/07 08:46:26
@@ -215,8 +215,8 @@
friend jint JvNumStaticFields (jclass);
friend jobject _Jv_AllocObject (jclass, jint);
- friend jobjectArray _Jv_NewObjectArray (jsize, jclass, jobject);
- friend jobject _Jv_NewPrimArray (jclass, jint);
+ friend void *_Jv_AllocObj (jint, jclass);
+ friend void *_Jv_AllocArray (jint, jclass);
friend jobject _Jv_JNI_ToReflectedField (_Jv_JNIEnv *, jclass, jfieldID,
jboolean);
Index: libjava/java/lang/natClassLoader.cc
===================================================================
RCS file: /cvs/java/libgcj/libjava/java/lang/natClassLoader.cc,v
retrieving revision 1.21
diff -u -r1.21 natClassLoader.cc
--- natClassLoader.cc 2000/03/09 09:22:36 1.21
+++ natClassLoader.cc 2000/05/07 08:46:26
@@ -572,6 +572,7 @@
vtable->clas = array_class;
memcpy (vtable->method, ObjectClass.vtable->method,
dm_count * sizeof (void *));
+ vtable->gc_descr = ObjectClass.vtable->gc_descr;
array_class->vtable = vtable;
array_class->vtable_method_count = ObjectClass.vtable_method_count;
@@ -584,6 +585,8 @@
array_class->interfaces = interfaces;
array_class->interface_count = 1;
+ // FIXME: Shouldn't this be synchronized? _Jv_PrepareConstantTimeTables
+ // needs to be called with the mutex for array_class held.
// Generate the interface dispatch table.
_Jv_PrepareConstantTimeTables (array_class);
/*
GCTest.java.
Run it with no arguments to loop through all tests, or a numerical
argument 1 thru 4 to loop on a single test.
*/
import java.util.*;
public class GCTest
{
static final int LOOPS = 10;
static final int ITERATIONS = 3;
static int finalized_count = 0;
final String str = "blah blah blah blah blah blah blah blah ...";
byte[] bytes = str.getBytes();
long startTime;
public static void main (String[] args)
{
new GCTest(args);
}
GCTest(String[] args)
{
if (args.length > 0)
{
int arg = Integer.parseInt(args[0]);
while(true)
{
start();
switch(arg)
{
case 1:
smallArrays();
break;
case 2:
bigArrays();
keep = new Vector(2500);
break;
case 3:
objArray();
break;
case 4:
finalization();
break;
}
stop();
}
}
for (int i=0; i < ITERATIONS; i++)
{
start(); smallArrays(); stop();
start(); bigArrays(); stop();
start(); objArray(); stop();
start(); finalization();
System.out.print(" [" + finalized_count + "] ");
stop();
finalized_count = 0;
System.out.println(" ---");
keep = new Vector(2500);
}
}
void smallArrays()
{
System.out.print("smallArrays() : ");
for (int i=0; i<LOOPS; i++)
{
System.out.print(".");
byte bufs[][] = new byte[200000][];
for (int j=0; j<bufs.length; j++)
{
bufs[j] = new byte[bytes.length];
System.arraycopy(bytes, 0, bufs[j], 0, bytes.length);
}
}
}
static final int ARRAY_SIZE = 500000;
Vector keep = new Vector(2500);
Random rand = new Random();
void bigArrays()
{
System.out.print("bigArrays() : ");
for (int i=0; i<LOOPS; i++)
{
System.out.print(".");
Byte[] buf = new Byte[ARRAY_SIZE];
for (int j=0; j<buf.length; j++)
{
buf[j] = new Byte((byte)0x012);
}
/* grab a few random bytes back and keep them somewhere */
/*
for (int j=0; j<250; j++)
{
int k = (rand.nextInt() & Integer.MAX_VALUE) % (ARRAY_SIZE-1);
keep.addElement(buf[k]);
}
*/
}
}
void objArray()
{
System.out.print("objArray() : ");
for (int i=0; i<LOOPS; i++)
{
System.out.print(".");
SmallObj objs[] = new SmallObj[250000];
for (int j=objs.length-1; j >= 0; j--)
{
objs[j] = new SmallObj();
}
}
}
void finalization()
{
System.out.print("finalization(): ");
for (int i=0; i<LOOPS; i++)
{
System.out.print(".");
FinalizeObj objs[] = new FinalizeObj[100000];
for (int j=objs.length-1; j >= 0; j--)
{
objs[j] = new FinalizeObj();
}
}
}
void start()
{
startTime = System.currentTimeMillis();
}
void stop()
{
//Runtime.getRuntime().gc();
long stopTime = System.currentTimeMillis();
System.out.print(" "+ (stopTime - startTime) + "ms");
System.out.print(" (heap: total=" + (Runtime.getRuntime().totalMemory()
/ 1024) + "K");
System.out.println(" free=" + (Runtime.getRuntime().freeMemory()
/ 1024) + "K)");
}
}
public class SmallObj
{
int a = 2;
int b = 3;
long c = 9996L;
char[] d = {'a','b','c','d','e'};
}
public class FinalizeObj extends SmallObj
{
public void finalize()
{
GCTest.finalized_count++;
}
}