Предотвращение утечек памяти Scala - конструкторы Scala

Как ответили и здесь : sup>

Самый простой способ ответить на этот вопрос - взглянуть на исходный код java.util.Date.

Он имеет только 2 нестатических поля (Java 1.7.0_55):

private transient long fastTime;
private transient BaseCalendar.Date cdate;

long имеет объем памяти 8 байтов, а cdate является ссылкой на объект, который имеет размер 4 байта. Таким образом, в общей сложности 12 байтов .

Если бы был создан экземпляр cdate, это могло бы потребовать дополнительных байтов в памяти, но если вы посмотрите и на конструкторы, иногда это даже не будет затронуто, а в других оно будет null -обработано в конец конструктора, поэтому конечный результат также составляет 12 байт .

Это только для создания Date. Если вы вызываете методы для Date (например, Date.toString()), то будет создавать и сохранять объект в поле cdate, которое не будет очищено. Поэтому, если вы вызовете определенные методы для Date, его использование памяти увеличится.

Примечание: Ссылки на объекты могут иметь длину 64 бита на 64-битных JVM, в этом случае использование памяти будет составлять 16 байтов.

Примечание № 2: Также обратите внимание, что это только использование памяти самого объекта Date. Скорее всего, вы будете хранить его ссылку где-нибудь, например. в массиве или списке или поле в каком-либо другом классе, для которого потребуются дополнительные 4 байта (или, может быть, 8 байтов на 64-битных JVM).

9
задан Flaviu Cipcigan 2 August 2009 в 13:09
поделиться

6 ответов

You could do this:

val numer = numerator / gcd(numerator.abs, denominator.abs)
val denom = denominator / gcd(numerator.abs, denominator.abs)

Of course you'd have to do the calculation twice. But then optimizations are often a trade-off between memory/space and execution time.

Maybe there are other ways too, but then the program might get overly complex, and if there's one place where optimization is rarely premature, it's brain power optimization :). For instance, you could probably do this:

val numer = numerator / gcd(numerator.abs, denominator.abs)
val denom = denominator / (numerator / numer)

But it doesn't necessarily make the code more understandable.

(Note: I didn't actually try this, so use at your own risk.)

6
ответ дан 4 December 2019 в 06:16
поделиться

Сопутствующий объект может обеспечить необходимую гибкость. Он может определять «статические» фабричные методы, заменяющие конструктор.

object Rational{

    def apply(numerator: Int, denominator: Int) = {
        def gcd(a: Int, b: Int): Int = if(b == 0) a else gcd(b, a % b)
        val g = gcd(numerator, denominator)
        new Rational(numerator / g, denominator / g)
    }
}

class Rational(numerator: Int, denominator: Int) {
  require(denominator != 0)

  override def toString  = numerator + "/" + denominator
  // other methods go here, neither access g
}

val r = Rational(10,200)

В рамках фабричного метода можно вычислять и использовать g для получения двух значений конструктора.

13
ответ дан 4 December 2019 в 06:16
поделиться

В примере Томаса Юнга есть небольшая проблема; он по-прежнему позволяет вам создавать объект Rational с общим термином в числителе и знаменателе - если вы создаете объект Rational с помощью 'new' самостоятельно, а не через сопутствующий объект:

val r = new Rational(10, 200) // Oops! Creating a Rational with a common term

Этого можно избежать, потребовав от клиентского кода всегда используйте сопутствующий объект для создания объекта Rational, сделав неявный конструктор закрытым:

class Rational private (numerator: Int, denominator: Int) {
    // ...
}
6
ответ дан 4 December 2019 в 06:16
поделиться

Вы можете сделать это:

object Rational {
    def gcd(a: Int, b: Int): Int =
        if(b == 0) a else gcd(b, a % b)
}

class Rational private (n: Int, d: Int, g: Int) {
    require(d != 0)

    def this(n: Int, d: Int) = this(n, d, Rational.gcd(n.abs, d.abs))

    val numer = n / g

    val denom = d / g

    override def toString = numer + "/" + denom

}
13
ответ дан 4 December 2019 в 06:16
поделиться

... на самом деле, я не понимаю, как это представляет собой "утечку памяти".

Вы объявляете последнее поле в рамках экземпляра класса, а затем явно удивляетесь что он «торчит». Какого поведения вы ожидали?

Я что-то упустил?

3
ответ дан 4 December 2019 в 06:16
поделиться

Я наткнулся на эту статью, которая может быть вам полезна: http://daily-scala.blogspot.com/2010/02/temporary-variables-during-object.html

Кажется, вы могли бы написать это:

class Rational(numerator: Int, denominator: Int) {
  require(denominator != 0)

  val (numer,denom) = {
      val g = gcd(numerator.abs, denominator.abs)
      (numerator/g, denominator/g)
  }

  override def toString  = numer + "/" + denom

  private def gcd(a: Int, b: Int): Int =
    if(b == 0) a else gcd(b, a % b)

  // other methods go here, neither access g
}
0
ответ дан 4 December 2019 в 06:16
поделиться
Другие вопросы по тегам:

Похожие вопросы: