堆内存和栈内存区别_堆上与堆外的内存使用情况

堆内存和栈内存区别

堆内存和栈内存区别

总览

最近有人问我在Java中使用堆内存的好处和智慧。 面临相同选择的其他人可能会对这些答案感兴趣。

堆外内存没什么特别的。 线程堆栈,应用程序代码,NIO缓冲区都在堆外。 实际上,在C和C ++中,您只有非托管内存,因为默认情况下它没有托管堆。 Java中托管内存或“堆”的使用是该语言的一个特殊功能。 注意:Java不是执行此操作的唯一语言。

新的Object()vs对象池vs Off堆内存

新的Object()

在Java 5.0之前,使用对象池非常流行。 创建对象仍然非常昂贵。 但是,从Java 5.0开始,对象分配和垃圾清理变得便宜得多,并且开发人员发现,通过删除对象池并仅在需要时创建新对象,它们可以提高性能,并简化代码。 在Java 5.0之前,几乎所有对象池(甚至是使用对象的对象池)都提供了改进,而从Java 5.0来看,只有昂贵的对象才有意义,例如线程,套接字和数据库连接。

对象池

在低延迟空间中,显而易见的是,通过减少CPU缓存的压力,回收可变对象可以提高性能。 这些对象必须具有简单的生命周期和简单的结构,但是使用它们可以看到性能和抖动方面的显着改善。

使用对象池的另一个有意义的领域是当使用许多重复的对象加载大量数据时。 通过显着减少内存使用量和减少GC必须管理的对象数量,您可以减少GC时间并提高吞吐量。

与使用同步HashMap相比,这些对象池的重量更轻,因此它们仍然可以提供帮助。

以这个StringInterner类为例。 您将所需文本的可循环使用的可变StringBuilder作为字符串传递给它,它将提供匹配的字符串。 传递String效率不高,因为您已经创建了对象。 StringBuilder可以回收。

注意:此结构具有一个有趣的属性,除了最低Java保证提供的安全性之外,不需要任何其他线程安全功能(例如volatile或sync)。 也就是说,您可以正确地看到String中的final字段,并且只能读取一致的引用。

public class StringInterner {
    private final String[] interner;
    private final int mask;
    public StringInterner(int capacity) {
        int n = Maths.nextPower2(capacity, 128);
        interner = new String[n];
        mask = n - 1;
    }

    private static boolean isEqual(@Nullable CharSequence s, @NotNull CharSequence cs) {
        if (s == null) return false;
        if (s.length() != cs.length()) return false;
        for (int i = 0; i < cs.length(); i++)
            if (s.charAt(i) != cs.charAt(i))
                return false;
        return true;
    }

    @NotNull
    public String intern(@NotNull CharSequence cs) {
        long hash = 0;
        for (int i = 0; i < cs.length(); i++)
            hash = 57 * hash + cs.charAt(i);
        int h = (int) Maths.hash(hash) & mask;
        String s = interner[h];
        if (isEqual(s, cs))
            return s;
        String s2 = cs.toString();
        return interner[h] = s2;
    }
}

堆外内存使用率

使用堆外内存和使用对象池都有助于减少GC暂停,这是它们唯一的相似之处。 对象池适用于短暂的可变对象,创建对象的开销很大,而存在很多重复的永久对象则是不可变的。 中度活泼的可变对象或复杂的对象更有可能由GC处理。 但是,中长寿命的可变对象遭受堆外内存解决的许多方式的困扰。

堆外内存提供;

  • 可扩展至大型内存,例如超过1 TB且大于主内存。
  • 名义上对GC暂停时间的影响。
  • 在进程之间共享,减少JVM之间的重复,并使分裂JVM更容易。
  • 持久性,可以更快地重新启动或回复测试中的生产数据。

在设计系统方面,堆外内存的使用为您提供了更多选择。 最重要的改进不是性能,而是确定性。

堆外和测试

高性能计算中的最大挑战之一是再现难以理解的错误,并能够证明已解决了这些错误。 通过以持久方式将所有输入事件和数据存储在堆外,您可以将关键系统变成一系列复杂的状态机。 (或者在简单的情况下,只有一个状态机)以这种方式,您可以在测试和生产之间获得可重现的行为和性能。

许多投资银行都使用此技术对一天中的任何事件可靠地重播系统,并确切地说明了为什么以这种方式处理该事件。 更重要的是,一旦你有了一个位置,你可以证明你有固定发生在生产,而不是发现问题,并希望这是问题的问题。

确定性行为与确定性行为一起出现。 在测试环境中,您可以按照实际的时间重播事件,并显示期望在生产中获得的延迟分布。 如果硬件不相同,某些系统抖动将无法重现,但是从统计角度看您可能会非常接近。 为了避免花一天时间重放一天的数据,您可以添加一个阈值。 例如,如果事件之间的时间超过10毫秒,则您可能只能等待10毫秒。 这样一来,您可以在不到一个小时的时间内按实际时间重播一天的事件,并查看您的更改是否改善了延迟分配。

通过降低级别,您不会失去“一次编译,随处运行”的某些功能吗?

从某种程度上讲,这是正确的,但远没有您想象的要多。 当您靠近处理器工作时,您将更加依赖处理器或OS的行为方式。 幸运的是,大多数系统使用AMD / Intel处理器,就其提供的低级别保证而言,甚至ARM处理器也变得更加兼容。 操作系统之间也存在差异,并且这些技术在Linux上比Windows上更有效。 但是,如果您在MacOSX或Windows上进行开发并使用Linux进行生产,则应该没有任何问题。 这就是我们在高频交易中所做的。

我们通过使用堆产生了哪些新问题?

没有什么是免费的,堆外情况就是这样。 堆外的最大问题是您的数据结构变得不太自然。 您或者需要一个可以直接映射到堆外的简单数据结构,或者需要一个进行序列化和反序列化以使其脱离堆的复杂数据结构。 显然使用序列化有其自身的麻烦和性能损失。 因此,使用序列化要比在堆对象上慢得多。

在金融世界中,最昂贵的数据结构是平坦且简单的,充满了原语,可以很好地在堆外映射,几乎没有开销。 但是,这并不适用于所有应用程序,您可以获得复杂的嵌套数据结构,例如图形,最终还必须在堆上缓存某些对象。

另一个问题是,JVM限制了您可以使用的系统数量。 您不必担心JVM会使系统过载太多。 有了堆外处理,就解除了一些限制,您可以使用比主内存大得多的数据结构,并且开始担心如果要这样做,您将拥有哪种磁盘子系统。 例如,您不希望分页到具有80 IOPS的HDD,而您可能希望拥有80,000 IOPS(每秒输入/输出操作)或更高(即快1000倍)的SSD。

OpenHFT如何提供帮助?

OpenHFT有许多库可以隐藏您实际上正在使用本机内存来存储数据的事实。 这些数据结构是持久的,可以使用很少甚至没有垃圾。 这些用于不需少量收集即可全天运行的应用程序中

编年史队列-持久的事件队列。 支持同一台机器上跨JVM的并发编写器和跨机器并发读取器。 微秒级的延迟和每秒数百万条消息的持续吞吐量。

历程图–键值图的本机或持久存储。 可以在同一台机器上的JVM之间共享,通过UDP或TCP复制和/或通过TCP远程访问。 微秒级延迟和持续的读/写速率,每台机器每秒执行数百万次操作。

线程亲和性–将关键线程绑定到隔离的内核或逻辑CPU,以最大程度地减少抖动。 可以将抖动降低1000倍。

使用哪个API?

如果您需要记录每个事件->编年史队列

如果您只需要最新结果才能使用唯一键->编年史地图

如果您关心20微秒抖动->线程亲和力

结论

堆外内存可能会带来挑战,但也会带来很多好处。 您可以在其中看到最大的收益,并与为实现可伸缩性而引入的其他解决方案进行比较。 与在堆缓存,消息传递解决方案或进程外数据库上使用分区/分片相比,堆外可能更简单,更快。 通过提高速度,您可能会发现不再需要为达到所需性能而需要做的一些技巧。 例如,堆外解决方案可以支持对OS的同步写入,而不必以异步方式执行写入操作,而存在数据丢失的风险。

但是,最大的好处就是启动时间,使您的生产系统重新启动的速度更快。 例如,映射到1 TB数据集中可能需要10毫秒,并且通过重播每个事件以使您每次都获得相同的行为,可以简化测试的可重复性。 这使您可以创建可以依靠的质量体系。

翻译自: https://www.javacodegeeks.com/2014/12/on-heap-vs-off-heap-memory-usage.html

堆内存和栈内存区别

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值