Reputation: 33
Application Description:
A recursive based Swing application that allows the visualization of a specific group of numbers I am researching. It uses a class called Node which has a recursive constructor, thus it is very memory intensive, since each Node creates a family of Nodes for itself, which it stores in a Vector
. The program itself just shows a tree graph that is generated by the Nodes themselves. (Keep in mind I limit the Nodes multiplication capacities)
My Problem:
The program is using way too much memory. I know that what is taking up memory are the Nodes, but I don't know how to get rid of them. I tried System.gc()
but that didn't work for memory management. (I am using standard Task Manager to verify how much RAM it's using)
I don't really need the Nodes themselves, I only need them to give me their BufferedImage
so I can use it in the program, but they stick around taking up a lot of memory.
Just to illustrate how much memory it's using, 4 different Nodes with a limit factor of 5 take up about 1 GB of RAM.
General Problem:
My real question and the question that interest most people is: "How do I get rid of declared objects that I'm not using anymore?"
Edit: The class Node is NOT inside the class I'm working with, they're just part of the same package.
Node Class:
package tailingprimes;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.*;
public class Node {
public BufferedImage canvas;
public double angle, radius, arm_size;
public int x,y,arms,limit;
public long value;
public Node parent;
public Vector<Node> children;
public Vector<Long> branch;
public static int side = 600;
public static double rad_scale = 0.3;
public static double arm_scale = 0.3;
public Node(Node nParent, long primeValue, int xPos, int yPos, double dAngle, int iLimit)
{
this.canvas = new BufferedImage(side,side,BufferedImage.TYPE_INT_RGB);
this.value = primeValue;
this.x = xPos;
this.y = yPos;
this.angle = dAngle;
this.limit = iLimit;
this.parent = nParent;
this.children = new Vector<Node>();
this.branch = TailingPrimePlus.primeBranch(value);
this.arms = this.branch.size();
Graphics ctx = this.rootCanvas().getGraphics();
if(nParent == null)
{
ctx.setColor(Color.WHITE);
ctx.fillRect(0, 0, 600, 600);
this.radius = 10;
this.arm_size = 150;
}
else
{
this.radius = rad_scale*parent.radius;
this.arm_size = arm_scale*parent.arm_size;
ctx.setColor(Color.GREEN);
ctx.drawLine(this.x, this.y, this.parent.x, this.parent.y);
this.arms++;
}
int real_radius = (int) Math.round(this.radius);
ctx.setColor(Color.BLACK);
ctx.drawOval(this.x-real_radius, this.y-real_radius, 2*real_radius, 2*real_radius);
if(limit > 0)
{
for(int t = 0; t < this.branch.size(); ++t)
{
double real_angle = this.angle + (t+1)*2*Math.PI/this.arms;
double real_distance = this.radius + this.radius*rad_scale + this.arm_size;
int x_now = this.x + (int) Math.round(real_distance*Math.cos(real_angle));
int y_now = this.y + (int) Math.round(real_distance*Math.sin(-real_angle));
Node now = new Node(this,this.branch.get(t),x_now,y_now,real_angle+Math.PI,this.limit-1);
this.children.add(now);
}
}
}
public BufferedImage rootCanvas()
{
if(parent==null)
{
return canvas;
}
else
{
return parent.rootCanvas();
}
}
public Node getNodeByTreeCode(long treeCode)
{
String text = Long.toString(treeCode);
int val = Integer.parseInt(text.substring(0,1)) - 1;
if(val >= children.size()){return null;}
if(text.length() > 1)
{
long next_val;
next_val = Long.parseLong(text.substring(1));
Node ans = children.get(val).getNodeByTreeCode(next_val);
return ans;
}
else
{
return children.get(val);
}
}
}
The JFrame Extension:
package tailingprimes;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.beans.DefaultPersistenceDelegate;
import javax.swing.*;
public class TailingPrimePlus extends JFrame implements ActionListener{
public int canvas_width = 600;
public int canvas_height = 600;
public static int current_step = 0;
public static long root = 1;
public JPanel p1 = new JPanel();
public JPanel p2 = new JPanel();
public JPanel p3 = new JPanel();
public JButton b1 = new JButton("1");
public JButton b2 = new JButton("3");
public JButton b3 = new JButton("7");
public JButton b4 = new JButton("9");
public JButton bclear = new JButton("Clear");
public JButton fwd = new JButton(">>");
public JButton rwd = new JButton("<<");
public JLabel step_text = new JLabel("Current Step: ");
public JLabel step_label = new JLabel("0");
{
b1.setActionCommand("1");
b2.setActionCommand("3");
b3.setActionCommand("7");
b4.setActionCommand("9");
bclear.setActionCommand("clear");
fwd.setActionCommand("fwd");
rwd.setActionCommand("rwd");
}
public JLabel image_holster = new JLabel();
public TailingPrimePlus()
{
setVisible(true);
setSize(new Dimension(800,800));
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
int xval = (1920-800)/2;
int yval = (1080-800)/2;
this.setBounds(xval, yval, 800, 800);
setLayout(new GridBagLayout());
GridBagConstraints cons = new GridBagConstraints();
cons.fill = 1;
cons.gridy = 0;
cons.weighty = 200;
add(p1, cons);
p1.setLayout(new GridBagLayout());
JPanel sub_1_1 = new JPanel();
p1.add(sub_1_1,cons);
sub_1_1.add(b1);
sub_1_1.add(b2);
sub_1_1.add(b3);
sub_1_1.add(b4);
b1.addActionListener(this);
b2.addActionListener(this);
b3.addActionListener(this);
b4.addActionListener(this);
JPanel sub_1_2 = new JPanel();
cons.gridy = 1;
p1.add(sub_1_2, cons);
sub_1_2.add(step_text);
sub_1_2.add(step_label);
cons.gridy = 1;
cons.weighty = 600;
add(p2, cons);
p2.add(image_holster);
cons.gridy = 2;
cons.weighty = 200;
add(p3,cons);
p3.add(rwd);
rwd.setEnabled(false);
rwd.addActionListener(this);
p3.add(fwd);
fwd.setEnabled(false);
fwd.addActionListener(this);
}
@Override
public void actionPerformed(ActionEvent e)
{
Node current = new Node(null,1,300,300,0,0);
String command = e.getActionCommand();
if(command.equals("1") || command.equals("3") || command.equals("7") || command.equals("9"))
{
long val = Long.parseLong(command);
root = val;
current = new Node(null,val,300,300,0,current_step);
BufferedImage next_canvas = current.canvas;
image_holster.setIcon(new ImageIcon(next_canvas));
rwd.setEnabled(true);
fwd.setEnabled(true);
}
else if(command.equals("fwd"))
{
current_step++;
current = new Node(null,root,300,300,0,current_step);
BufferedImage next_canvas = current.canvas;
image_holster.setIcon(new ImageIcon(next_canvas));
}
else if(command.equals("rwd"))
{
current_step--;
current = new Node(null,root,300,300,0,current_step);
BufferedImage next_canvas = current.canvas;
image_holster.setIcon(new ImageIcon(next_canvas));
}
current = null;
step_label.setText(Integer.toString(current_step));
validate();
}
public static HashMap<Long, Long> safeBranchOut(long root, int limit)
{
HashMap<Long, Long> tree = new HashMap<Long,Long>();
long cursor = 0;
branchOut(root, limit, cursor, tree);
return tree;
}
public static void branchOut(long root, int limit, long cursor, HashMap<Long, Long> tree)
{
long current_root = root;
int current_limit = limit;
if(cursor < Math.pow(10, limit-1))
{
Vector<Long> current_branch = primeBranch(current_root);
for(int t = 0; t < current_branch.size(); ++t)
{
++cursor;
current_root = current_branch.get(t);
tree.put(cursor, current_root);
cursor *= 10;
branchOut(current_root, current_limit, cursor, tree);
cursor /= 10;
}
}
}
public static boolean isPrime(double num)
{
if(num == 1) {return false;}
else if(num == 2) {return true;}
else if(num%2 == 0) {return false;}
else
{
for(double t = 3; t <= Math.sqrt(num); t+=2)
{
if(num%t==0 && num!=t)
{
return false;
}
}
return true;
}
}
public static Vector<Long> primeBranch(Long root)
{
Vector<Long> ans = new Vector<Long>();
for(int t = 1; t <= 9; ++t)
{
String text = root.toString();
text = t + text;
long abs = Long.parseLong(text);
if(isPrime(abs))
{
ans.addElement(abs);
}
}
return ans;
}
public static void main(String args[])
{
new TailingPrimePlus();
}
}
This is what the tree looks like
UPDATE:
I installed VisualVM and I discovered that the field int[]
in the memory sampler is holding all of the memory "leakage". If I had to guess, this has to do with the Vectors
I've been using, but I'm not sure. Does anyone know what it means and how to handle it?
Upon closer inspection, on a test using VisualVM, I verified this:
VisualVM: Thread Name: AWT-EventQueue-0 Alocated Bytes: 467.286.672 (90.1%)
Upvotes: 0
Views: 328
Reputation: 33
PROBLEM SOLVED!
I managed to solve my problem!
In the Node constructor I simply replaced
this.canvas = new BufferedImage(600,600,BufferedImage.TYPE_INT_RGB)
with
if(nParent == null){this.canvas = new BufferedImage(side,side,BufferedImage.TYPE_INT_RGB);}
since only the first Node requires a canvas (rootCanvas()
).
In the end, I was wrong about the memory allocation issues, it turned out to have nothing to do with the nodes, I just needed to realize that I was creating empty BufferedImages
that are pretty heavy.
Thanks for the time and patience of all the people involved, I hope this turns out to be useful to someone else.
Upvotes: 0
Reputation: 361
If you have a class that contains some useless objects, the garbage collector will not collect them because they are still being referenced, but you can consider assigning null
to these variables.
Upvotes: 2