Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

Memory Use/Garbage Collection: Java vs Perl

by c0d3cr33p (Acolyte)
on Sep 02, 2002 at 20:48 UTC ( [id://194634]=perlquestion: print w/replies, xml ) Need Help??

c0d3cr33p has asked for the wisdom of the Perl Monks concerning the following question:

I ran the following code:
//======================================================= import java.io.*; class dup { public static void main(String args[]) { dupme ob = new dupme(0); }// end main() }// end class dup class dupme { public dupme(int me) { System.out.println("I am '" + me + "'!"); dupme ob = new dupme(me + 1); }// end interface dumpe(int me) }// end class dupme() //=======================================================
After 200,000 iterations and before 300,000 iterations, it simply dies. Spits out over 30kb of errors into a log file. Why does this happen? The equivallent code in perl...
#======================================================= package main; my $ob = dupme->new(0); package dupme; sub new { my $s = {}; bless $s; print "I am '$_[1]'!\n"; my $ob = $s->new($_[1]+1); return $s; }# end sub new #=======================================================
...ran faster (on my machine) and did not exit until 853865 iterations. I realize this is comparing apples to horses, but what does this tell me? Considering the way objects are started, exist and are destroyed in each VM is quite different, I realize this is the result of perhaps thousands of differences and 2 completely different development perspectives. On windows98, AMD500, 296Mb RAM, the java one ran only to just over 4000 iterations, while the perl version went beyond 2,000,000 iterations! I wonder why, if Java is supposed to have such slick garbage collection, does it have _any_ kind of problem?

Replies are listed 'Best First'.
Re: Memory Use/Garbage Collection: Java vs Perl
by BrowserUk (Patriarch) on Sep 02, 2002 at 21:24 UTC

    I don't think garbage collection has anything to do with this at all.

    In both cases, each sub doesn't return until its child has returned. As there is no limiting mechanism, no child ever returns! Therefore nothing is ever marked (unmarked) as free, so the garbage collector never has a chance to do anything?

    Basically, you are simply recursing off the end of the stack in both programs.

    If you want to test garbage collection, you need to throw some objects away, so that the gc has something to do.

    One possible (and I emphasis possible, I know nothing of the internals of either) cause for some of the difference, is that Java is threadsafe. This implies a rather heavier stack usage than with non-threadsafe Perl?

    In the Java version you are also concatenating string objects. Three times!

    You start with the string (constant) "I am '", this is copied and concatenated with the stringified me, this is then copied to another and concatenated with the string "'!". That's 5 strings being created to print that one line. You should be using a StringBuffer to build this string. Or simply outputing the 3 bits as seperate strings. This is the most basic, and frequent mistake Java beginners make.

    The Perl version will use it DWIM and intelligent compiling to build a single entity at the same point.

    Like you said, a strong case of horses eating apples!


    Well It's better than the Abottoire, but Yorkshire!

      ...you're right about this BrowserUK. Garbage collection doesn't get a chance to shine here. This was really a stack overflow test.

      To answer part of the originally posed question here, yes, Java does have some problems with it. Expect object overhead to be greater than in Perl. (In Perl, we simply string some things together and declare them an object. And that's very appropriate for our style of doing things.)

      In Java, there is a significant workload associated with creation of an object. Issues such as security and multithreading are important in that setting.

      Were we to address some of the same concerns in Perl, we'd have to provide that infrastructure for ourselves. And that's okay. It would probably be "just as much as we needed," for that is the strategy for object creation in Perl.

      Each has its place. If I have to put five people to work designing and implementing parts of a system that will have to be integrated, I think I want them using a discipline that enforces somewhat rigid conventions.

      If I have to put one really bright programmer to work at implementing a system that will be running quickly, I want a programming model that gives me just as much as I need, and stated in just about any fashion that makes sense to me.

      Or perhaps:

      ++$perl if $rapid_development > 2; # we need it fast ++$perl if $developers <= 2; # we have a small dev team ++$java if $threadsafe > 2; # we plan heavy thread use ++$java if $foreign_objects > 2; # untrusted objects print $perl >= $java ? "perl" : "java";

      You can use Perl for anything under the sun. But perhaps not everyone should try.

      ...All the world looks like -well- all the world, when your hammer is Perl.
      ---v

        I agree with some reservations.

        A good systems analyst (Note: Singular, not 'design commitee'!), should be able to partition pretty much any system into chunks and allocate those chunks to teams of two, who then exclusively (of other people rather than projects) work on designing and implementing those chuncks.

        I say teams of two (up to 4 I think is ok) rather than gifted individuals, as I think even the most gifted individual benefits from and sometimes needs someone to point out his assumptions and blind-spots.

        We've all encountered the lone-programmer who can churn out reams of working, tight, efficient code. They often have the ability to juggle several projects simulatenously. However, with rare exceptions, I would rather not be the one that has to go back and try to modify their code.

        The whole design-by-commitee thing is a real bug-bear of mine. My favorite example of why software should never be designed by committee, is the english language.

        Designed by (one-of) the biggest 'committies' in history, it is an eternally fascinating subject for philosophers, writers, critics, historians et al. (eg. pretty much the whole BA side of learning), but it has no real place except that of defacto-standard in most BSc. subject areas.

        This is exampled by the whole 'the sound of ...ough' thing (with tough, though, through, bough, cough, hough, lough).

        Personally I don't (yet) see any real reason a large project shouldn't be written in Perl, given proper analysis up front and on-going peer review of the code, but then I'm still learning (Perl).


        Well It's better than the Abottoire, but Yorkshire!
      I originally was writing a question as to how throwing away string objects in Java was handled, and whether or not the 4 unused strings out of the 5 created were destroyed, but then I tripped over this line in the J2SE 1.4 API docs: "The Java language provides special support for the string concatentation operator ( + ), and for conversion of other objects to strings. String concatenation is implemented through the StringBuffer class and its append method. String conversions are implemented through the method toString, defined by Object and inherited by all classes in Java." I did some digging on java.sun.com, and that line is verbatim in every API dating back to JDK v1.1. So it appears as though a println() call using the + operator looks like this:
      System.out.println(((new StringBuffer("I am'")).append(me.toString())) +.toString());
      to the compiler (the last toString() because a PrintStream doesn't have a println() method that takes a StringBuffer). I was taught even in my first CS class (we learned Java) that strings worked the way you said, and I was under that assumption until about five minutes ago. Perhaps I am wrong, but in my reading of the docs it appears that Java handles string concatentation with the + operator as you suggested he try manually.

        You make a very good point! My bad on that bit.

        <excuses>

        I did say <cite>I think...</cite>

        What do you expect at a Perl site? Java expertise :8^)...

        </excuses>

        Being serious for a moment, if Javac is 'doing the right thing' on that line, that just means that it's memory overhead on recursive subroutines must be even worse than I thought. 4,000 -v- 2,000,000 recursions before blowing the stack is a mind-boggling 500 times less efficient in terms of memory/stack management. I guess I was looking for a rational explanation.

        Note: I have made no attempt to verify the numbers

        Update:I have confirmed that your assertion is true when using javac from jdk1.3.1 as follows

        import java.io.PrintStream; class dupme { public dupme(int i) { // 0 0:aload_0 // 1 1:invokespecial #1 <Method void Object()> // 2 4:getstatic #2 <Field PrintStream System.out> // 3 7:new #3 <Class StringBuffer> // 4 10:dup // 5 11:invokespecial #4 <Method void StringBuffer()> // 6 14:ldc1 #5 <String "I am '"> // 7 16:invokevirtual #6 <Method StringBuffer StringBuffe +r.append(String)> // 8 19:iload_1 // 9 20:invokevirtual #7 <Method StringBuffer StringBuffe +r.append(int)> // 10 23:ldc1 #8 <String "'!"> // 11 25:invokevirtual #6 <Method StringBuffer StringBuffe +r.append(String)> // 12 28:invokevirtual #9 <Method String StringBuffer.toSt +ring()> // 13 31:invokevirtual #10 <Method void PrintStream.println +(String)> // 14 34:new #11 <Class dupme> // 15 37:dup // 16 38:iload_1 // 17 39:iconst_1 // 18 40:iadd // 19 41:invokespecial #12 <Method void dupme(int)> // 20 44:astore_2 // 21 45:return } }

        Well It's better than the Abottoire, but Yorkshire!
Re: Memory Use/Garbage Collection: Java vs Perl
by zaimoni (Beadle) on Sep 03, 2002 at 03:21 UTC

    Java doesn't garbage-collect until the variable 'exits scope'. While Perl's garbage-collection has very ill-defined timing, it can collect anything that is syntactically unusable.

    This makes the print statement (and its Java analog) the most likely issue.

    1. In Java, you have two string concatenations. All three initial strings and both concatenations will be fully allocated until they 'leave scope'. If you don't carefully place braces so that the temporary variables leave scope before the recursive call, they will be allocated until the program crashes. Did you carefully place those braces in the Java version?
    2. In Perl, both implicit temporaries (if indeed, they are distinct rather than a cleverly reallocated single) are collectable the instant the print statement is over.

    So a clumsy Java translation will literally permanently allocate more memory on each recursion. The overhead of Sun's JVM is also much larger than the overhead for Perl. For the more recent ones, Sun recommends assuming 64MB of RAM just to launch their JVM.

    Also, Java finalizers execute asynchronously, and are not guaranteed to be executed after the last access of the data object they finalize. A priori, the Java crash could be due to the program attempting to use a finalized (garbage-collected) object. (Perl has similar issues when globally shutting down modules -- but this is after the Perl program is finished.)

Re: Memory Use/Garbage Collection: Java vs Perl
by the_Don (Scribe) on Sep 03, 2002 at 14:24 UTC
    Do you know, or how can you determine the stack size that each implementation uses? It may just be a matter of allocation here that is making Java look so inefficient.

    My point (which is off topic with regards to garbage collection because I also agree with the others posts that gc is not an issue here) is to either set the stack size specifically (if possible) or to find the currently used stack size and compute that into the efficiency evaluation. After you factor stack size, then see what the results are. It will not be apples to apples, but not as bad as apples to horses... maybe like apples to ponies.

    I now must apologize for offering a solution, but not leaving directions on how to carry out my hypothesis. I do not know how to see or manage stack size in Java or Perl. I like to bring up a lot of possibilities but not work through the evaluation... sounds like my professors in college.

    the_Don
    ...making offers others can't rufuse.

      Perl is "stackless", meaning it doesn't actually use the system stack to store any of its interpreter state (in the absence of XS code re-entering the interpreter to do callbacks and such). All of Perl's stacks are allocated out of the heap.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://194634]
Approved by toma
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others avoiding work at the Monastery: (7)
As of 2024-04-19 09:24 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found