0

You should not use the Files.readAllBytes class when converting the file to bytes. After using different byte converter. The main problem is that the byte array in the variable cannot be deleted from memory.

example 1

byte[] x1=new byte[50];
x1=null; //not memory deleting;

example 2

byte[] x2=null;   //memory space:0
x2=new byte[50];  //memory space:50
x2=new byte[100]; //memory space:150
x2=new byte[10];  //memory space:160
x2=null;          //memory space:160

I realized this was the situation.

how can we clear this from memory space? There were dynamic memory allocation methods in C language (Malloc), this memory is not deleted in java.

    package dragdrop;
import java.awt.EventQueue;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.List;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.TransferHandler;
import java.awt.BorderLayout;
import java.awt.Color;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

public class gfgh {

private JFrame frame;
private File_Class file_Class=null;
private JPanel panel;

/**
 * Launch the application.
 */
public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
        public void run() {
            try {
                gfgh window = new gfgh();
                window.frame.setVisible(true);              

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });
}

/**
 * Create the application.
 */
public gfgh() {
    initialize();
}

/**
 * Initialize the contents of the frame.
 */
private void initialize() {
    frame = new JFrame();
    frame.setBounds(100, 100, 450, 300);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

     panel = new JPanel();
    panel.setBackground(Color.BLACK);
    frame.getContentPane().add(panel, BorderLayout.CENTER);

    JButton btnNewButton = new JButton("New button");
    btnNewButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {


            memorydelete();
        }
    });
    panel.add(btnNewButton);
    this.DragDrop(panel);

}

private  void DragDrop(JComponent component)
{
    @SuppressWarnings("serial")
    TransferHandler transferHandler=new TransferHandler(){

         @Override
         public boolean canImport(JComponent component,DataFlavor[] dataFlavor)
         {
             return true;
         }
         @SuppressWarnings("unchecked")
        @Override
        public boolean importData(JComponent comp, Transferable t) {
            // TODO Auto-generated method stub
             List<File> files;
            try {
                files =  (List<File>) t.getTransferData(DataFlavor.javaFileListFlavor);

                for(File file:files)
                 {
                     System.out.println(file.getAbsolutePath());
                     System.out.println(file.length());
                     file_Class=new File_Class();
                     //file read examle 1 false
                    //  file_Class.file=Files.readAllBytes(Paths.get(file.getAbsolutePath()));


                     //* file read example 2 false
                     /* 
                     RandomAccessFile f = new RandomAccessFile(file.getAbsolutePath(), "r");
                      file_Class.file = new byte[(int)f.length()];
                      f.readFully( file_Class.file);
                       */
                      //example 3  false
                    // file_Class.file=readFile(file);

                    //example 4  false
                     file_Class.file=readFile(file);
                     memorydelete();

                     ///////
                 }
                files=null;

                Runtime.getRuntime().gc();
            } catch (UnsupportedFlavorException e) {

                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

            return true;
        }
    };
    component.setTransferHandler(transferHandler);
}
public void memorydelete()
{
    file_Class.file=null;
    file_Class=null;
    Runtime.getRuntime().gc();
}
public static byte[] readFile(File file) throws IOException {
    // Open file
    RandomAccessFile f = new RandomAccessFile(file, "r");
    try {
        // Get and check length
        long longlength = f.length();
        int length = (int) longlength;
        if (length != longlength)
            throw new IOException("File size >= 2 GB");
        // Read file and return data
        byte[] data = new byte[length];
        f.readFully(data);
        return data;
    } finally {
        f.close();
    }
}
}
class File_Class {
public byte file[];
}
Ogün
  • 97
  • 2
  • 10
  • In the code you posted, you call method `gc()` in class `Runtime`. Do you understand how garbage collection works in java? – Abra May 12 '20 at 04:24
  • Q: _How to delete variable from memory?_ A: You can't. You've already done the most you can do: you've set the sole variable that was referencing your byte array to `null`, so that the byte array is now cut off from any way your Java program has of getting to it. From this time forward, the memory occupied by the byte array is liable to being reclaimed and re-used at any instant, but the JVM's garbage collector alone decides when (and whether) that actually happens. – Kevin Anderson May 12 '20 at 04:37
  • Abra yes. new byte[memory]; each created new byte object is occupying memory. It is becoming a growing stack. – Ogün May 12 '20 at 04:45
  • Kevin Anderson : None of these happen, I observed one by one, it is not deleted from memory. – Ogün May 12 '20 at 04:46

2 Answers2

2

The main problem is that the byte array in the variable cannot be deleted from memory.

No. That's not the problem.

All you need to do is to arrange that the byte array is unreachable by assigning null to the variable ... and any other reachable variable that still holds a reference to the array.

The garbage collector (GC) will take care of it.

You DON'T need to call System.gc(). The GC will run when it needs to run. If you try intervene, you will probably only make your code inefficient.

See: Why is it bad practice to call System.gc()?


The real problem is one of the following:

  1. The file is too big to read into memory; i.e. so big that you are getting an OOME. If this is the case, the solution is to stream the file rather than using readAllBytes.

  2. You are obsessing over memory utilization. Memory is cheap. Programmer time is expensive.

If you really need to minimize the memory usage of your application, you are using the wrong programming language. Java is not designed for this style of programming. Java and Java garbage collectors are designed to be efficient in cases where the GC runs infrequently and there is plenty of spare memory to hold garbage between collections. If you try to fight that, your application is liable to waste lots of time reclaiming small amounts of memory.

Note that even if the Java GC does collects your unreachable byte array (almost) immediately, it probably won't return the reclaimed memory to the operating system. So your application's memory footprint won't shrink. So you most likely don't get any real benefit from calling System.gc() explicitly.

If you really need to minimize memory usage, use C or C++

If you really need to explicitly delete objects, use C or C++

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
0

In Java, arrays are objects. The Java garbage collector will eventually take care of them and will reclaim memory associated with unreferenced objects.

You can easily test this as follows:

public class Test {
     public static void main(String[] args) {
        long m1 = Runtime.getRuntime().freeMemory();
        byte[] data = new byte[1024*1024*4];
        long m2 = Runtime.getRuntime().freeMemory();
        data = null;
        System.gc();
        long m3 = Runtime.getRuntime().freeMemory();
        System.out.println("m1: " + m1);
        System.out.println("m2: " + m2);
        System.out.println("m3: " + m3);
     }
}

The output (absolute values will differ in your envirionment):

m1: 15887208
m2: 11692888
m3: 15972272

As you can see, triggering the garbage collection explicitly here reclaims the resources associated with the 4 MB byte array. Now, usually, its neither required, nor productive/efficient to trigger the garbage collector explicitly (and there's actually no guarantee that doing so triggers a collection). In the example, this is done for illustrative purposes.

Garbage collection runs in the background, the JVM schedules it how it sees fit. One strategy is to run a full collection whenever the heap space is (almost) exhausted. Things are more complicated because different kinds of heaps usually exist for different generations of objects (e.g. new objects vs. old objects).

The most important thing you can do is not to keep references to unused objects around. Eventually, those unreferenced objects will be collected and the associated memory will be reused.

horstr
  • 2,377
  • 12
  • 22
  • *"Oh my God !!!!!!! "* - I could say the same thing about the time that people have to *waste* debugging heap corruption bugs in C / C++ code :-) – Stephen C May 12 '20 at 05:02
  • 1
    Calling `System.gc()` does not trigger a garbage collection, it just suggests one, see the [docs](https://docs.oracle.com/javase/7/docs/api/java/lang/System.html). The garbage collector can decide to ignore this suggestion. Of course this does not mean that the space is never reclaimed, just you could not trigger it with guarantees. – CoronA May 12 '20 at 05:16
  • That is true, that's why I said "there's actually no guarantee that doing so triggers a full collection". I removed the "full" collection reference as there's also no guarantee that it triggers any kind of collection. – horstr May 12 '20 at 05:42