java 清空list的内存_java – 清空后单个链表仍然占用内存

我的问题是,在添加项目然后删除它们之后,java程序仍然使用与以前相同的内存.

这是一个简单的例子

Deque 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 implements Iterable {

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 iterator() {

return new DequeIterator();

}

private void debug() {

Iterator 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 {

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 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 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 {

Node 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值