Another Experience using GCJ as an embedded compiler

Jonathan P. Olson olson@mmsi.com
Tue Mar 30 23:47:00 GMT 1999


On 1999年3月30日, you wrote:
Greetings, Geetha,
Good to hear that somebody else is working with GCJ. When I first started
this project I had some doubts about using Java in an embedded environment.
Now, I see it as an excellent high-level embedded language that can produce
code nearly equivalent in quality to C++.
A particularly interesting development in this project was the addition of
real-time garbage collection to our embedded C++ OS environment. By
adding write barriers to both C++ and Java compilers, I have been able
to embed GC deeply into our embedded environment. Now every byte
of memory, whether allocated by C++, assembler, or Java, gets allocated
through GC. GC gets all of physical memory! No more memory leaks
anywhere. Pretty cool for an embedded environment.
>After this, we looked at the following issues:
>>* Exception handling:
> As you had very rightly pointed out, the sjlj approach to exception handling in
>GCJ needed modification. However, instead of modifying the data structure as you did, we
>took the approach of expanding the code generated after every catch of an exception to
>check for exception type and rethrow if required. This also eliminated the second jmpbuf
>required for the finally clause.

For code density, I determined that an entirely fresh approach to exception
handling was required. In my implementation, exception handling and monitor
support are tightly integrated. Whereas the standard implementation requires
a system call to perform monitor locking, and much more inline and setjmp/
longjmp calls. Synchronized methods now just require a single call
to enter the monitor and another call to exit the monitor. Period. No setjmp/
longjmp or exception tables required.
Similarly, try/catch blocks generated enormous volumes of code using setjmp/
longjmp, and in-line manipulation of the exception stack. Now, there's
just a single __catch call at the start of a try block, a single __catch_pop
at the end of a try block, and an single exception table for all catch
blocks. Really shrunk the code big time and decreased the stack requirements
alot.
>>* Automating the list of classes:
> I noticed a discussion on this list some time ago about the difficulties in
>deciding all the classes to compile natively. We have developed a tool to find all the
>static/dynamic class references from an application and invoke the GCJ on these classes
>to generate a library. Here we modified a tool that we already had and it takes care of
>the classes loaded through 'forName' too.

I've been considering such a tool but with Java it seems that it might produce
poor results. Problem is that with polymorphism, you don't know what method
is really getting called. For example, if anybody anywhere references anything
that calls object->toString(), then all toString methods must get included.
And Java is FULL of toString() methods that are useless except for debugging
purposes.
In linking up a Java library, it seems that the Java API is full of gratuitous
cross references that prevent meaningful subsetting of the API. For example,
String.valueOf(anything). In my opinion, String shouldn't require the Double
class or 64-bit Long class to run, but in Java it does. Bad design in my
opinion.
Anyway, I'd be interested in what success you've had in automatically generating
libraries that are Java subsets. How are you doing this? Do you create some
sort of `.mclass' file like Sun speaks of in their Embedded Java promos?
I assume that you're doing the dependencies with the .class files and not
on the compiled binaries...
>>* Dynamic loading of Java classes: (GCJ + JAVA)
> Co-existance of native code and Java code was our next focus. While the 'closed
>compilation' of classes required by Cygnus is good and produces efficient code, we loose
>the dynamic loading capability of Java that is specially required for embedded systems
>that would act as application servers. To bring in this, we enhanced on the GCJ front
>end with a "-CJI" or "-java" option. CJI for Cygnus Java Interface. This option is used
>by GCJ to generate heavier code at calling points (invoke interface and invoke virtual).
>We now have an environment that takes a set of classes that are gcj compiled and works
>working well even if the classes loaded thru' forName() are NOT GCJ compiled.
>
Actually, our embedded OS supports dynamic loading of arbitrary C++ code
modules from the network. The code modules that I create using g++ and gcj
are totally position independent, pure-text, rommable modules that can be
dynamically loaded via any connected network. Don't really need a JVM.
Works better to just run the compiler to build a binary on demand.
In this way, gcj on the host just becomes a fancy class loader, although
the modules traveling across the network become native code.
>* Exception handling in mixed environment:
> Currently, we are testing the exception handling in this mixed environment.
>Trying to catch an exception in Native code that is thrown by Java code and vice versa,
>with multiple nested calls intersperced, etc
>
Yeah, I want to get my exception changes integrated in with g++ so I can
throw exceptions between languages. I can now throw exceptions from
C++ to Java, but I need to use the following funky syntax:
	_Jv_Throw(::classMetaData)
Can't yet catch Java exceptions from C++, though. Problem with C++ exceptions
in general, though, is that C++ allows you to throw anything, even by value.
You can throw an `int', `double' or `FooBar'. And when throwing an object,
C++ allocates the object in the receiver's stack space. My druthers is to
make it so that C++ must always catch a Throwable object by reference
just like Java does. C++ exceptions are just plain broken by design. Nice
thing is that with garbage collection integrated into the C++ and operating
system, you don't have to worry about memory leaks when throwing
exceptions. Also it eliminates most of the need for most C++ destructors and
all the associated messy cleanup code for calling destructors in-case an
exception gets thrown.
> Our immediate future plan is to break the 'closed compilation' requirement of the
>GCJ approach - and allow selective compilation of classes. We have the design for this
>and plan to take up the implementation after the above step. Even though this will
>result in a loss of performance to some extent, we beleive that there is a need for a
>compromise between speed and dynamic nature of the application.

So you're planning on having both compiled Java code plus a JVM? Ok, I guess,
if you really need to load and run class files straight from the Internet and
necessary if you're writing an embedded browser. However, for typical embedded
applications, I find that the ability to dynamically load native binaries from a
host is plenty of flexibiity. As long as you have a class load host to run the
compiler, it can appear that you're just loading class files.
--
Jon Olson, Modular Mining Systems
	 3289 E. Hemisphere Loop
	 Tucson, AZ 85706
olson@mmsi.com
Phone:	(520)746-9127
Fax:	(520)889-5790


More information about the Java mailing list

AltStyle によって変換されたページ (->オリジナル) /