如何减少在Scala中创建的对象的数量?(How to reduce the number of o

2019-08-03 09:10发布

我在编程斯卡拉它采用了RGB类的图像中的点返回颜色的计算机图形应用程序。 你可以想像,这退回的颜色RGB对象的函数被调用多次。

class RGB(val red: Int, val green: Int, val blue: Int) { }

存在被经常使用如下的功能getPixelRGB

val color:RGB = getPixelRGB(image, x, y)

问题是,我可以调用这个函数一百万次,这将那么,我相信,产生一百万独特的RGB对象实例,那是一个很不起眼的局面。 有一些想法我对这个:

  1. getPixelRGB可能创建对象的无限数量如果它被称为的无数次,但它不必是对象的无限数量,因为只有一个最大的可用于RGB产生255 * 255 * 255种可能的组合。 因此,创建的对象的数量“应该”是有限的。 此功能可以调整为使用对象池,其中如果是返回相同的颜色,一段时间之后才能够为颜色返回相同的池对象实例。

  2. 我可以编码此RGB的诠释。 一个int会对内存开销比正常的斯卡拉/ Java对象少,Java对象有额外的内存开销。 由于Scala的int类型是4个字节宽,前3个字节可以存储的RGB值。 只有返回一个诠释,而不是从getPixelRGB方法的RGB会较少的内存开销我承担。 但是如何做到这一点,同时仍然具有说服RGB类的?

  3. 据说,他们是,短暂的对象,我已阅读,垃圾收集器应迅速重新要求他们。 不过我还是担心。 如何在GC知道我迅速带走扔呢? 如此混乱。

因此,在一般情况下,我的问题是如何使这一getPixelRGB更多的内存友好吗? 也应我甚至担心吗?

Answer 1:

您可以用单一的长编码RGB或INT 。 此外,在斯卡拉2.10可以定义值类的原始值,说

class RGB private(val underlying: Long) extends AnyVal {
  def toTriple = /*decoding to (red, green, blue)*/
} 
object RGB {
  def apply(red: Int, green: Int, blue: Int) = /* encode and create class with new RGB(longvalue)*/
}

对于值类,你仍然可以有类型信息,并在JVM享受类较少的内存布局。



Answer 2:

你的问题#3还没有解决,所以我会试一试。

如何在GC知道我扔[短暂的对象]远快?

现代GC的工作是根据不同的生命周期的表现非常不同的是物体的观察。 因此,管理它们在所谓的世代 。 刚刚创建的对象存储在伊甸园空间。 当这个填满了,在它所有的对象目前仍在被引用(即他们还活着)被复制到所谓的年轻一代的空间。 因此,所有的死亡对象留下和他们所占用的空间几乎为零的努力回收。 这是什么使短暂的对象如此便宜的JVM。 而最平均程序创建的对象是,它们很快掉出的范围暂时或局部变量。

第一轮GC之后,年轻一代的空间(一个或多个)以类似的方式进行管理,但可能有更多的人。 GC能够被配置为拥有对象花费一个或多个回合的年轻一代用空间(S)。 然后最后,最后的幸存者迁移到幸存者 (又名老一代 )的空间,他们在那里呆了他们一生的其余部分。 这个空间由周期性地施加的经典标志和扫描技术的一些变体中进行管理:通过所有活动引用的图形行走和标记的活动对象,然后通过压实幸存者成为一个连续的存储器块扫出所有未标记的(死的)对象,从而碎片整理的可用内存。 这是一个昂贵的操作,其阻断程序的执行,这是非常难以正确实现它,尤其是在现代多线程虚拟机。 这就是为什么代GC被发明出来,以确保只有创建的所有对象的一小部分到达这个阶段。



Answer 3:

在内存友好方面,最有效的解决方案是完整的色彩信息存储只是一个诠释。 正如你所提到的正确,颜色信息只需要三个字节,所以四个字节Int的就足够了。 你可以编码和使用位运算解码来自一个诠释的RGB信息:

def toColorCode(r: Int, g: Int, b: Int) = r << 16 | g << 8 | b

def toRGB(code: Int): (Int, Int, Int) = (
  (code & 0xFF0000) >> 16, 
  (code & 0x00FF00) >> 8, 
  (code & 0x0000FF)
)


Answer 4:

据说,他们是,短暂的对象,我已阅读,垃圾收集器应迅速重新要求他们。 不过我还是担心。 如何在GC知道我迅速带走扔呢? 如此混乱。

它不知道它。 它假定它。 这就是所谓的在所有代垃圾收集器都是建立在假设代

  • 几乎所有对象英年早逝
  • 几乎没有旧的对象包含对新对象的引用

满足这一假设的对象是非常便宜(甚至更便宜,其实比mallocfree的语言如C语言),只有违反一个或两个假设是昂贵的对象。



Answer 5:

你可以有一个返回一个简单的界面Int 。 然后,你可以使用隐式转换治疗的IntRGB对象需要的地方。

case class RBGInt(red: Int, green: Int, blue: Int) {
   // ...
}

object Conversions { 

  implicit def toRGBInt(p: Int) = {
    val (r, g, b) = /* some bitmanipulation to turn p into 3 ints */
    RGBInt(r, g, b)
  }

}

然后,你可以把任何IntRGBInt ,你觉得很有道理:

type RGB = Int // useful in documenting interfaces that consume
               // or returns Ints which represent RGBs

def getPixelRGB(img: Image, x: Int, y: Int): RGB = {
  // returns an Int
}

def someMethod(..) = {
  import Conversions._
  val px: RGB = getPixelRGB(...) // px is actually an Int
  px.red // px, an Int is lifted to an RGBInt
}


文章来源: How to reduce the number of objects created in Scala?