简体   繁体   English

JVM:如何管理JNI创建的堆外内存

[英]JVM: How to manage off-heap memory created by JNI

I'm building a Scala wrapper around the Torch libraries. 我正在围绕Torch库构建一个Scala包装器。 I'm using Swig to build the glue layer. 我正在使用Swig构建胶层。 It allows me to create tensors off-heap, which I can only free by explicitly calling a static method of the library. 它允许我在堆外创建张量,我只能通过显式调用库的静态方法来释放它。 However, I want to use the tensors in an imperative way, without having to worry about releasing the memory, much like any ordinary object in Java. 但是,我想以命令的方式使用张量,而不必担心释放内存,就像Java中的任何普通对象一样。

The only way I can think of doing this is to (mis-)use the JVM's garbage collector in the following way: 我能想到这样做的唯一方法是以下列方式(错误地)使用JVM的垃圾收集器:

A 'memory manager' keeps track of the amount of off-heap memory consumed, and when a threshold is reached, it calls System.gc() . “内存管理器”跟踪消耗的堆外内存量,当达到阈值时,它会调用System.gc()

object MemoryManager {    
  val Threshold: Long = 2L * 1024L * 1024L * 1024L // 2 GB
  val FloatSize = 4
  private val hiMemMark = new AtomicLong(0)

  def dec(size: Long): Long = hiMemMark.addAndGet(-size * FloatSize)
  def inc(size: Long): Long = hiMemMark.addAndGet(size * FloatSize)

  def memCheck(size: Long): Unit = {
    val level = inc(size)
    if (level > Threshold) {
      System.gc()
    }
  }
}

The tensors themselves are wrapped in a class, with a finalize method, that frees the off-heap memory, like so: 张量器本身包含在一个类中,使用finalize方法释放堆外内存,如下所示:

class Tensor private (val payload: SWIGTYPE_p_THFloatTensor) {
  def numel: Int = TH.THFloatTensor_numel(payload)

  override def finalize(): Unit = {
    val memSize = MemoryManager.dec(numel)
    TH.THFloatTensor_free(payload)
  }    
}

Tensor creation is done by a factory method, that notifies the memory manager. 张量创建由工厂方法完成,该方法通知内存管理器。 For example, to create a Tensor of zeros: 例如,要创建零的Tensor:

object Tensor {
  def zeros(shape: List[Int]): Tensor = {
      MemoryManager.memCheck(shape.product)
      val storage = ... // boilerplate
      val t = TH.THFloatTensor_new
      TH.THFloatTensor_zeros(t, storage)
      new Tensor(t)
  }
}

I realize this is a naive approach, but can I get away with this? 我意识到这是一种天真的方法,但我可以逃脱这个吗? It seems to work fine, also when running in parallel (which generates lots of superfluous calls to System.gc() but otherwise nothing) Or can you think of a better solution? 它似乎工作正常,也是在并行运行时(它会产生大量对System.gc()的多余调用,但除此之外什么都没有)或者你能想到一个更好的解决方案吗?

Thank you. 谢谢。

There's a more deterministic option - explicitly managed regions of memory 有一个更确定的选项 - 明确管理的内存区域

So, roughly if we had a class like this: 所以,大致如果我们有这样一个类:

class Region private () {
  private val registered = ArrayBuffer.empty[() => Unit]
  def register(finalizer: () => Unit): Unit = registered += finalizer
  def releaseAll(): Unit = {
    registered.foreach(f => f()) // todo - will leak if f() throws
  }
}

We could have a method implementing so-called "Loan pattern" that gives us a fresh region and then handles the deallocation 我们可以有一个实现所谓的“贷款模式”的方法,它给我们一个新的区域,然后处理释放

object Region {
  def run[A](f: Region => A): A = {
    val r = new Region
    try f(r) finally r.releaseAll()
  }
}

Then something that requires manual deallocation could be described as taking an implicit Region : 然后需要手动释放的东西可以被描述为采用隐式Region

class Leakable(i: Int)(implicit r: Region) {
  // Class body is constructor body, so you can register finalizers
  r.register(() => println(s"Deallocated foo $i"))

  def foo() = println(s"Foo: $i")
}

Which you would be able to use in a fairly boilerplate-free way: 您可以以相当无样板的方式使用它:

Region.run { implicit r =>
  val a = new Leakable(1)
  val b = new Leakable(2)
  b.foo()
  a.foo()
}

This code produces the following output: 此代码生成以下输出:

Foo: 2
Foo: 1
Deallocated foo 1
Deallocated foo 2

Such approach is limiting a little bit (if you try to assign a Leakable to a variable outside the closure passed in run , its scope will not be promoted), but will be faster and guaranteed to work even if calls to System.gc are disabled. 这种方法有一点限制(如果你尝试将Leakable分配给在run传递的闭包之外的变量,它的范围将不会被提升),但是即使禁用对System.gc调用,它也会更快并保证可以工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM