Hashset 使用不当引起的内存泄漏

本文通过一个实例详细解析了在Java中使用HashSet时如何因为修改已存储对象的哈希值相关属性而导致内存泄漏的问题。展示了对象属性更改后哈希值的变化会使得对象无法被正确移除,从而留在集合中造成内存泄漏。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

修改hashset中对象的属性值,且属性值是计算哈希值的字段,这时会引起内存泄漏

即:当一个对象被存储进HashSet集合中以后,就不能修改该对象的参与计算哈希值的属性值了
,否则对象修改后的哈希值与最初存储进HashSet集合中时的哈希值就不同了,在这种情况下,即使在contains方法使用该对象的当前引用作为参数去HashSet集合中检索对象,也将返回找不到对象的结果,这也会导致无法从HashSet集合中删除当前对象,造成内存泄露。

举例说明:


public class HashSetTest {
    public static void main(String[] args) {
        Set<Person> set = new HashSet<Person>();
        Person p1 = new Person("唐僧", "pwd1", 25);
        Person p2 = new Person("孙悟空", "pwd2", 26);
        Person p3 = new Person("猪八戒", "pwd3", 27);
        set.add(p1);
        set.add(p2);
        set.add(p3);
        System.out.println("总共有:" + set.size() + " 个元素!"); //结果:总共有:3 个元素!  
        p3.setAge(2); //修改p3的年龄,此时p3元素对应的hashcode值发生改变  
        set.remove(p3); //此时remove不掉,造成内存泄漏  
        set.add(p3); //重新添加,居然添加成功  
        System.out.println("总共有:" + set.size() + " 个元素!"); //结果:总共有:4 个元素!  
        for (Person person : set) {
            System.out.println(person);
        }

    }
}


public class Person {
    private String username;
    private String password;
    private int    age;

    public Person(String username, String password, int age) {
        this.username = username;
        this.password = password;
        this.age = age;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + age;
        result = prime * result + ((password == null) ? 0 : password.hashCode());
        result = prime * result + ((username == null) ? 0 : username.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Person other = (Person) obj;
        if (age != other.age)
            return false;
        if (password == null) {
            if (other.password != null)
                return false;
        } else if (!password.equals(other.password))
            return false;
        if (username == null) {
            if (other.username != null)
                return false;
        } else if (!username.equals(other.username))
            return false;
        return true;
    }

    @Override
    public String toString() {
        return this.username + "-->" + this.password + "-->" + this.age;
    }

}
### Unity 打包程序报错 Unexpected mark stack overflow 的解决方案 #### 可能原因分析 在Unity项目中遇到`Unexpected mark stack overflow`错误通常与垃圾回收器(GC)有关。此问题可能由多种因素引起: - **递归调用过深**:如果存在无限或非常深层次的递归调用,则可能导致栈空间耗尽,进而触发该异常[^2]。 - **对象图复杂度过高**:当场景中的游戏物体及其组件间相互引用形成极其复杂的网络结构时,在进行标记清理阶段可能会超出可用堆栈大小限制。 - **内存泄漏**:未释放不再使用的资源也可能间接影响到GC的行为模式,造成不必要的压力累积直至崩溃发生。 #### 解决方法建议 针对以上提到的原因,可以采取如下措施来尝试解决问题: 1. **审查并优化代码逻辑** 对所有可能存在深层嵌套循环或者递归算法的部分进行全面检查,确保其终止条件合理有效;对于确实需要频繁迭代的情况考虑改写成非递归形式实现相同功能。 2. **简化场景和资产** 尽量减少单个场景内活跃的游戏物件数量以及它们之间互相依赖关系的数量级,特别是那些带有大量事件监听器或其他动态行为的对象实例化频率应加以控制。 3. **调整Garbage Collector参数配置** 虽然默认设置适用于大多数情况,但在特定条件下适当调节一些高级选项或许有助于缓解症状。例如启用增量式收集策略以分散一次性大规模扫描带来的冲击力。 4. **排查潜在的内存泄露源** 使用Profiler工具监测整个应用生命周期内的分配趋势变化曲线,定位可疑位置后逐一验证是否存在不当持有强引用阻止了及时清除无用数据的现象。 5. **更新引擎版本至最新稳定版** 开发团队持续改进着产品性能表现及稳定性修复各类已知缺陷,因此升级往往能够带来意想不到的效果改善体验质量。 ```csharp // 示例:将深度优先遍历改为广度优先遍历避免过度消耗栈帧 public void BFS(Node root){ Queue<Node> queue = new Queue<Node>(); HashSet<Node> visitedNodes = new HashSet<Node>(); queue.Enqueue(root); while(queue.Count != 0){ Node currentNode = queue.Dequeue(); if(!visitedNodes.Contains(currentNode)){ ProcessNode(currentNode); // 处理节点 foreach(var child in currentNode.Children){ queue.Enqueue(child); } visitedNodes.Add(currentNode); } } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值