java – 清空后单个链表仍然占用内存
作者:互联网
参见英文答案 > How to free memory in Java? 13个
> Does GC release back memory to OS? 4个
> Does the JVM give back free memory to the OS when no longer needed? 3个
> java free memory usages 1个
> Limit jvm process memory on ubuntu 2个
对于作业,我们被要求使用单个链表实现数据结构.
我的问题是,在添加项目然后删除它们之后,java程序仍然使用与以前相同的内存.
这是一个简单的例子
Deque<Integer> deck = new Deque<>();
for( int i =0; i < 500000;i++) {
deck.addFirst(i);
}
for( int i =0; i < 500000;i++) {
deck.removeFirst();
}
System.out.println("end"); //Debugger here
添加50万个项目会消耗27mb的内存.
移除它们后仍然是27mb.
在最后使用调试器跳转,变量组的字段first = null
和count = 0;
为什么会这样? deck是空的,没有任何项目,但内存仍然像以前一样使用.
代码通过了所有正确性测试但在内存上失败了.
我还看了一步一步的调试器,并正在做应该做的事情.
可能是在removeFirst()中我没有设置任何null为空而只是首先分配为first.next?
编辑:这是内存测试的输出
Test 10: Total memory usage after inserting 4096 items, then successively
deleting items, seeking values of n where memory usage is maximized
as a function of n
n bytes
---------------------------------------------------
=> passed 3200 65592
=> passed 1600 65592
=> FAILED 800 65592 (1.7x)
=> FAILED 400 65592 (3.4x)
=> FAILED 200 65592 (6.7x)
=> FAILED 100 65592 (13.1x)
=> FAILED 50 65592 (25.3x)
==> 2/7 tests passed
正如您所看到的,50个元素仍在使用65592
EDIT2:
// remove and return the item from the front
public Item removeFirst() {
if (this.isEmpty()) throw new java.util.NoSuchElementException();
Item current = first.Item;
first = first.Next;
count--;
return current;
}
这是removeFirst()
EDIT3:
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.concurrent.TimeUnit;
/**
* Created by Alex on 6/30/2018.
*/
public class Deque<Item> implements Iterable<Item> {
private Node first;
private int count;
private class Node {
private Node Next;
private Item Item;
}
// construct an empty deque
public Deque() {
first = null;
count = 0;
}
// is the deque empty?
public boolean isEmpty() {
if (count == 0) {
return true;
}
return false;
}
// return the number of items on the deque
public int size() {
return count;
}
// add the item to the front
public void addFirst(Item item) {
if (item == null) throw new IllegalArgumentException();
Node temp = new Node();
temp.Item = item;
temp.Next = first;
first = temp;
count++;
}
// add the item to the end
public void addLast(Item item) {
if (item == null) throw new IllegalArgumentException();
if (isEmpty()) {
addFirst(item);
} else {
Node last = first;
while (last.Next != null) {
last = last.Next;
}
Node newLast = new Node();
newLast.Item = item;
newLast.Next = null;
last.Next = newLast;
count++;
}
}
// remove and return the item from the front
public Item removeFirst() {
if (this.isEmpty()) throw new java.util.NoSuchElementException();
Item current = first.Item;
first = first.Next;
count--;
return current;
}
// remove and return the item from the end
public Item removeLast() {
if (isEmpty()) throw new java.util.NoSuchElementException();
if (size() == 1) {
return removeFirst();
}else {
Node newLast = first;
Node oldLast = first;
while (oldLast.Next != null) {
newLast = oldLast;
oldLast = oldLast.Next;
}
newLast.Next = null;
count--;
// Item lastItem = ;
return oldLast.Item;
}
}
// return an iterator over items in order from front to end
public Iterator<Item> iterator() {
return new DequeIterator();
}
private void debug() {
Iterator<Item> deckIter = iterator();
while(deckIter.hasNext()) {
System.out.println(deckIter.next());
}
System.out.println(isEmpty());
System.out.println("Size:" + size());
}
// an iterator, doesn't implement remove() since it's optional
private class DequeIterator implements Iterator<Item> {
private Node current = first;
public boolean hasNext() { return current != null; }
public void remove() { throw new UnsupportedOperationException(); }
public Item next() {
if (!hasNext()) throw new NoSuchElementException();
Item item = current.Item;
current = current.Next;
return item;
}
}
// unit testing (optional)
public static void main(String[] args) throws InterruptedException {
Deque<Integer> deck = new Deque<>();
for( int i =0; i < 500000;i++) {
deck.addFirst(i);
}
for( int i =0; i < 500000;i++) {
deck.removeFirst();
}
System.out.println("end");
TimeUnit.MINUTES.sleep(5);
}
}
Edit4:那些是内存限制
谢谢.
解决方法:
从我的测试看来,内存仍然被分配,但等待垃圾收集. java.lang.Runtime类有一些方法可以告诉你内存使用情况,还有一个静态方法gc()来建议运行垃圾收集.这是一个额外的方法和main()类的修改,可以帮助您:
private final static long MB = 1024*1024;
static void memStats(String when)
{
Runtime rt = Runtime.getRuntime();
long max = rt.maxMemory();
long tot = rt.totalMemory();
long free = rt.freeMemory();
System.out.printf(
"mem(mb): %5d tot, %5d free %5d used %5d max %s\n",
tot/MB, free/MB, (tot-free)/MB, max/MB, when);
}
// unit testing (optional)
public static void main(String[] args) {
Deque<Integer> deck = new Deque<>();
memStats("startup");
for( int i =0; i < 500000;i++) {
deck.addFirst(i);
}
memStats("after alloc");
for( int i =0; i < 500000;i++) {
deck.removeFirst();
}
memStats("after removal");
Runtime.getRuntime().gc();
memStats("after gc");
System.out.println("end");
try {
TimeUnit.MINUTES.sleep(5);
} catch (InterruptedException ex)
{
}
}
将那些代替main()放在你的Deque类中并运行它.我明白了:
mem(mb): 15 tot, 15 free 0 used 247 max startup
mem(mb): 29 tot, 10 free 19 used 247 max after alloc
mem(mb): 29 tot, 10 free 19 used 247 max after removal
mem(mb): 29 tot, 29 free 0 used 247 max after gc
end
如您所见,(1)分配给Java的内存从Java对象的15MB总分配开始,使用0MB. (忽略最大数字.这没有报告我的想法.)它在分配后使用了19个,其中19个是不计其数.
在释放Deque中的所有节点之后,立即没有任何变化!
但是,在gc()调用之后,请注意分配给堆的总内存没有更改,但现在所有内存都可供其他Java对象使用.垃圾收集最终会发生 – 通常只要堆中没有足够的连续内存来满足新请求.
正如我在评论中所说,添加到堆中的额外14MB左右没有返回到操作系统,我并不感到惊讶.如果在Windows中使用任务管理器,则该内存仍将被视为“正在使用”.转向操作系统获取内存是很昂贵的,所以通常情况下这只是为了扩展大块(大约1500万字节,在这种情况下,似乎)并且只在运行结束时发布.但这是依赖于实现的行为,并且在不同的JVM上可能会有所不同.
关于代码的说明.我将Node类转换为:
private static class Node<Item> {
Node<Item> next; // lowercase next
Item item; // You were using Item for both a type and field name!?
}
…几乎每次出现节点转换为Node< Item>.这是应该的,因为节点项类型是通用的.在同一份报告中,这将内存使用量从19MB减少到15MB.非静态内部类将内部类的每个实例绑定到周围类的特定实例.你不需要它,它需要时间和内存.
该修复可能有助于您通过测试.
哦,是的,请注意字段名称从下一个更改为下一个,项目更改为项目.如果要符合传统的Java命名样式,请使用小写字母启动字段和方法名称.在任何情况下,您都不应该为字段名称和泛型类型名称使用Item.
标签:java,memory,data-structures,singly-linked-list 来源: https://codeday.me/bug/20190710/1425328.html