Solved

JRE 1.4.1 memory usage

Posted on 2004-08-10
24
3,947 Views
Last Modified: 2013-12-22
The following application allows the user to allocate a chunk of memory and then null the reference to the memory and "suggest" garbage collection.  My question is, why does the garbage collector take such a long time to collect the memory?
I can allocate 5,000,000 bytes and then suggest garbage collection.  The garbage collector takes its sweet time collecting the memory.  If I have an application that allocates a large amount of memory but then de-allocates it, the JRE retains the memory for a very long period of time and my app looks like a memory hog.  Most users don't notice this but if you look at TaskManager it looks like my application is using a huge amount of memory.  What gives?  Should I switch to .NET?

//system imports
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.Timer;

//project imports


//local imports

/** This nifty application lets me play around with garbage collection. */
public class MemTest extends JFrame
{
    //members
    /** The garbage collection button. */
    private JButton mGcButton = new JButton("GC");
    /** The allocation button. */
    private JButton mAllocateButton = new JButton("Allocate");
    /** The max memory label. */
    private JLabel mMaxMemoryLabel = new JLabel("Max Memory:");
    /** The total amount of memory used by the JRE. */
    private JLabel mTotalMemoryLabel = new JLabel("Total Memory:");
    /** The amount of free memory. */
    private JLabel mFreeMemoryLabel = new JLabel("Free Memory:");
    /** The array of allocated memory. */
    private byte[] mAllocatedMemory;
   
    /** Create the memory test application. */
    public MemTest()
    {
        setTitle("Memory Test");
        setSize(220, 260);
       
        getContentPane().setLayout(new GridLayout(5, 1));
       
        getContentPane().add(mGcButton);
        getContentPane().add(mAllocateButton);
        getContentPane().add(mMaxMemoryLabel);
        getContentPane().add(mFreeMemoryLabel);
        getContentPane().add(mTotalMemoryLabel);
       
       
        //listen for button clicks
        ActionListener ButtonListener = new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                if(e.getSource().equals(mGcButton))
                {
                    System.out.println("garbage collection suggested");
                    mAllocatedMemory = null;
                    System.gc();
                }
                else if(e.getSource().equals(mAllocateButton))
                {
                    String InputValue = JOptionPane.showInputDialog(MemTest.this, "Please input a value");
                    try
                    {
                        mAllocatedMemory = new byte[Integer.parseInt(InputValue)];
                    }
                    catch(Exception Ex)
                    {
                        Ex.printStackTrace();
                    }
                }
            }
        };
        mGcButton.addActionListener(ButtonListener);
        mAllocateButton.addActionListener(ButtonListener);
       

        ActionListener MemoryPollTimer = new ActionListener()
        {
            public void actionPerformed(ActionEvent evt)
            {
                mMaxMemoryLabel.setText("Max Memory: " + Runtime.getRuntime().maxMemory());
                mFreeMemoryLabel.setText("Application Memory: " + (Runtime.getRuntime().totalMemory() + Runtime.getRuntime().freeMemory()));
                mTotalMemoryLabel.setText("Used Memory: " + Runtime.getRuntime().totalMemory());
            }
        };
        new Timer(1000, MemoryPollTimer).start();
       
        addWindowListener(new WindowAdapter()
        {
            public void windowClosing(WindowEvent e)
            {
                System.exit(0);
            }
        });
        setLocationRelativeTo(null);
    }
   
    /** The main loop of the application. */
    public static void main(String args[])
    {
        new MemTest().setVisible(true);
    }
}
0
Comment
Question by:nealgoodman
  • 8
  • 6
  • 5
  • +4
24 Comments
 
LVL 92

Expert Comment

by:objects
ID: 11768089
> My question is, why does the garbage collector take such a long time to collect the memory?

Probably because it doesn't need it.

Calling gc() is just a hint, the vm is free to ignore that request.
0
 
LVL 15

Expert Comment

by:Javatm
ID: 11768096

This is the correct way of finalizing and calling gc(), try it :

protected void finalize()  {
  super.finalize();

  // Refer all the objects you want to null
  object = null;

  // Then call the garbage collector
  System.gc();
}

Hope that helps . . .
Javatm
0
 
LVL 15

Expert Comment

by:Javatm
ID: 11768102
0
 
LVL 13

Expert Comment

by:Webstorm
ID: 11768105
Hi nealgoodman,

Calling the System.gc() may help but it only say that the garbage collector can be done now.
The unused memory areas are only actually released when memory is needed.
0
 
LVL 15

Expert Comment

by:Javatm
ID: 11768138
> Calling the System.gc() may help but it only say that the garbage collector can be done now.
Thats correct, until you finalize the objects !
0
 
LVL 13

Expert Comment

by:Webstorm
ID: 11768181
>> Thats correct, until you finalize the objects !
Yes, but the finalize() is called by the garbage collector (source: the link you posted).
0
 
LVL 15

Expert Comment

by:Javatm
ID: 11768255
Thats correct and its needed in every class to propose a finalize method to free up every
memory consumption aside from calling system.gc();
0
 
LVL 15

Expert Comment

by:Javatm
ID: 11768262
How did you come up with a name webstorm ? very interesting =-) thanks.
0
 
LVL 1

Author Comment

by:nealgoodman
ID: 11769113
I realize that System.gc suggests that the garbage collector run and does not guarantee anything.  It is just frustrating that the JRE hangs on to unused memory for such a long time.  There doesn't appear to be a way to force the memory to be deallocated.  Sun assumes that the Garbage Collector is smarter than the programmer.  Most of the time it is smarter, but not always.

Javatm
I have never seen any documentation that says you have to call finalize to force deallocation of memory, are you sure about this?

Thanks for all of the quick responses.

0
 
LVL 92

Expert Comment

by:objects
ID: 11769141
You can decrease the memory footprint of your application if you want gc() to kick in earlier.
0
 
LVL 15

Expert Comment

by:Javatm
ID: 11769175
I've seen several finalize method in several java app's and I've seen that also in many books. Finalize method is used to tell the JVM that it needs
to collect all objects which is inside finalize for garbage collection. Try it and it will add help . . .
0
 
LVL 15

Expert Comment

by:Javatm
ID: 11769186
An old document but provides information :
http://java.sun.com/developer/TechTips/2000/tt0124.html#tip1

Hope that helps . . .
Javatm
0
Do You Know the 4 Main Threat Actor Types?

Do you know the main threat actor types? Most attackers fall into one of four categories, each with their own favored tactics, techniques, and procedures.

 
LVL 15

Expert Comment

by:Javatm
ID: 11769219
This is another version for better gc invoker :

protected void finalize()  throws Throwable {

   try {
   // Refer all the objects you want to null
   object = null;
   }  

   finally {
   super.finalize();
   }
}
0
 
LVL 30

Expert Comment

by:mayankeagle
ID: 11770460
I guess there is some more GC support in the JDK 1.5. Have a look at the java.lang.management package.
0
 
LVL 1

Expert Comment

by:justywong
ID: 11770471
if u want to know more about gc and how to optimize it, I suggest you read this doc:

http://java.sun.com/docs/hotspot/gc1.4.2/index.html

0
 
LVL 1

Accepted Solution

by:
SlimHealer earned 125 total points
ID: 11775569
You asked: "Why does the garbage collector take such a long time to collect the memory?"

By and large, I think you understand the real answer -- the garbage collector thinks it has a better understanding of memory allocation than you do.  In fact, it might be right.   It might be right (in general) even though it works pretty poorly in the example you give above.

The example you detail above isn't actually typical of a normal running application.  Research indicates that most applications need garbage collection to very efficiently clean up relatively small, fairly short lived objects. To manage this correctly, the garbage collector must wait long enough between minor collections so that it can distinquish short lived objects from long lived objects.

Early java garbage collectors managed this poorly.  They used pretty simplistic heuristics to trigger garbage collection, and many of the original "mark & sweep" implementations would in fact immediately respond to a System.gc() request by giving back all the memory that it could.  

Unfortunately, these garbage collectors had to freeze the running app while they thought about what was live and what was dead, and this made things like responsiveness really bad.

More modern garbage collectors (like the one in HotSpot) use a generational scheme which separates allocations into different chunks of memory based on the creation time and lifetime of an object.  

One of the goals of this sort of garbage collector is to avoid expensive "full garbage collection" sweeps, and to instead do periodic, very quick minor collections.

Further complicating the situation is the timing of the allocation and de-allocation of the chunks of memory that the JRE uses to store each generation of objects.  

There's a tricky tradeoff involved.  If the JRE hands back a big chunk of memory too quickly, it is likely to have to ask the system for the same memory right back again.  If it hands it back too slowly, then it is a memory hog -- hanging onto objects that it no longer needs.

There's loads more information available.  Here's one very practical bit of advice: run some java apps with the command line argument

  -verbose:gc

This can be very helpful in understanding how GC affects an applications performance.

You asked:

"What gives?  Should I switch to .NET?"

Nothing.  No -- at least not because of the behavior of the garbage collector.

0
 
LVL 1

Author Comment

by:nealgoodman
ID: 11776908
Thanks slim

I figured that was the answer I would get.  I wish Java would give you the option of manually allocated and deallocating memory in certain circumstances.  I wish this was one of the JCP issues.  It is hard to sell Java as a development platform for desktop applications when you look at task manager and see a simple application using 14 MB.  An GIS application we developed recently uses 140MB at times. Runtime.getRuntime().totalMemory() claims that we are only using 60MB.
0
 
LVL 92

Expert Comment

by:objects
ID: 11779241
> see a simple application using 14 MB.

Then just reduce its footprint if it doesn't need that much.
0
 
LVL 1

Author Comment

by:nealgoodman
ID: 11780085
>Then just reduce its footprint if it doesn't need that much.
That is the whole point of this question!  How do I reduce the memory footprint?!  Runtime.getRuntime().totalMemory() indicates that the application is not using 14mb but TaskManager says different.
0
 
LVL 92

Expert Comment

by:objects
ID: 11780101
java -Xms<initialheap> -Xmx<maxheap> ...
0
 
LVL 1

Author Comment

by:nealgoodman
ID: 11784348
java -Xms2m -Xmx8m MemTest

I tried this with the above code and it still shows 14mb being used by the JRE.


-Xmsn
Specify the initial size, in bytes, of the memory allocation pool. This value must be a multiple of 1024 greater than 1MB. Append the letter k or K to indicate kilobytes, or m or M to indicate megabytes. The default value is 2MB. Examples:
       -Xms6291456
       -Xms6144k
       -Xms6m
       

-Xmxn
Specify the maximum size, in bytes, of the memory allocation pool. This value must a multiple of 1024 greater than 2MB. Append the letter k or K to indicate kilobytes, or m or M to indicate megabytes. The default value is 64MB. Examples:
       -Xmx83886080
       -Xmx81920k
       -Xmx80m

0
 
LVL 92

Expert Comment

by:objects
ID: 11788903
I think you are getting the heap size confused with the OS process size, they are two different things
0
 
LVL 1

Author Comment

by:nealgoodman
ID: 11792386
You are correct, they are two very different things.  The heap size is the only thing you can actually control.  You can use -Xmx or -mx to set the max allowed heap size.  Even then the GC will hang on to unused memory and only it can determine when to deallocate that memory.  I guess the thing that bothers me most is that the JRE is a memory hog.  In the case of the 14MB app, the JRE says the application is only using 6MB.  If you look at the JRE process in Task Manager, we seem to be using 14MB.  I don't think there is anything the developer can do about that.  Even the examples from Sun (SwingSet2 48MB, Metalworks 27MB) cause the JRE to use large amounts of memory.  Working in a software development group that develops mostly with C++ these seem like extraordinary amounts of memory for such simple applications.  This coupled with the "Java is slow" myth gives Java a bad reputation.
0
 
LVL 1

Author Comment

by:nealgoodman
ID: 11794494
It looks like Sun has addressed part of the JRE memory footprint in 1.5.  They claim that they are continuing to reduce the memory footprint.


http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4607284

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4466510

0

Featured Post

What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
how to use external config file with Spring MVC 4 61
Fibonacci challenge 11 82
network + 7 73
Java - Why doesn't this JFrame work 3 17
An old method to applying the Singleton pattern in your Java code is to check if a static instance, defined in the same class that needs to be instantiated once and only once, is null and then create a new instance; otherwise, the pre-existing insta…
INTRODUCTION Working with files is a moderately common task in Java.  For most projects hard coding the file names, using parameters in configuration files, or using command-line arguments is sufficient.   However, when your application has vi…
Viewers learn about the “while” loop and how to utilize it correctly in Java. Additionally, viewers begin exploring how to include conditional statements within a while loop and avoid an endless loop. Define While Loop: Basic Example: Explanatio…
Viewers will learn about if statements in Java and their use The if statement: The condition required to create an if statement: Variations of if statements: An example using if statements:

706 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

20 Experts available now in Live!

Get 1:1 Help Now