Spark内存管理-UnifiedMemoryManager和StaticMemoryManager

2023-11-12

  在Spark-1.6.0中,引入了一个新的参数spark.memory.userLegacyMode(默认值为false),表示不使用Spark-1.6.0之前的内存管理机制,而是使用1.6.0中引入的动态内存分配这一概念。

  从SparkEnv.scala的源码中可以看到,该参数设置为true或false,主要影响到构造memoryManager的类的不同:

val useLegacyMemoryManager = conf.getBoolean("spark.memory.useLegacyMode" , false)
val memoryManager: MemoryManager =
  if (useLegacyMemoryManager) {
    new StaticMemoryManager(conf , numUsableCores)
  } else {
    UnifiedMemoryManager(conf , numUsableCores)
  }

  StaticMemoryManagerUnifiedMemoryManager都是从MemoryManager抽象类继承而来,类继承关系如下图:
  
  Spark中MemoryManager类

  下面根据这三个类的具体实现,基本上就可以了解到这两种MemoryManager的不同之处了。

一、MemoryManager

在抽象类MemoryManager中有一些实现了的变量或者方法,

1. 变量

(1)tungstenMemoryMode
  final变量,根据spark.memory.offHeap.enabled参数(默认为false)来决定是ON_HEAP还是OFF_HEAP

final val tungstenMemoryMode: MemoryMode = {
    if (conf.getBoolean("spark.memory.offHeap.enabled", false)) {
      require(conf.getSizeAsBytes("spark.memory.offHeap.size", 0) > 0,
        "spark.memory.offHeap.size must be > 0 when spark.memory.offHeap.enabled == true")
      MemoryMode.OFF_HEAP
    } else {
      MemoryMode.ON_HEAP
    }
  }

  如果设置为true,则为OFF_HEAP,但同时要求参数spark.memory.offHeap.size(默认为0),设置为大于0的值。设置为默认值false,则为ON_HEAP
(2)tungstenMemoryAllocator
  final变量,根据上面变量模式来选择是使用HeapMemoryAllocator,还是使用UnsafeMemoryAllocatorON_HEAP对应HEAPOFF_HEAP对应Unsafe

  private[memory] final val tungstenMemoryAllocator: MemoryAllocator = {
    tungstenMemoryMode match {
      case MemoryMode.ON_HEAP => MemoryAllocator.HEAP
      case MemoryMode.OFF_HEAP => MemoryAllocator.UNSAFE
    }
  }

(3)storageMemoryPool
  是一个StorageMemoryPool类型的变量,从名字上看是一个storage内存池。

 protected val storageMemoryPool = new StorageMemoryPool(this)

(4)onHeapExecutionMemoryPool和offHeapExecutionMemoryPool
  这两个都是ExecutionMemoryPool类型,只是在名称上有不同的识别,一个是“on-heap execution”,一个是”off-heap execution”。对应execution内存池。

protected val onHeapExecutionMemoryPool = new ExecutionMemoryPool(this, "on-heap execution")
  protected val offHeapExecutionMemoryPool = new ExecutionMemoryPool(this, "off-heap execution")

(5)pageSizeBytes
  大小由参数spark.buffer.pageSize决定,默认值有一个计算逻辑:default值由变量(4)的大小除以核数,再除以safetyFactor(16)得到的最接近的2的幂值,然后限定在1M64M之间。

  val pageSizeBytes: Long = {
    val minPageSize = 1L * 1024 * 1024   // 1MB
    val maxPageSize = 64L * minPageSize  // 64MB
    val cores = if (numCores > 0) numCores else Runtime.getRuntime.availableProcessors()
    // Because of rounding to next power of 2, we may have safetyFactor as 8 in worst case
    val safetyFactor = 16
    val maxTungstenMemory: Long = tungstenMemoryMode match {
      case MemoryMode.ON_HEAP => onHeapExecutionMemoryPool.poolSize
      case MemoryMode.OFF_HEAP => offHeapExecutionMemoryPool.poolSize
    }
    val size = ByteArrayMethods.nextPowerOf2(maxTungstenMemory / cores / safetyFactor)
    val default = math.min(maxPageSize, math.max(minPageSize, size))
    conf.getSizeAsBytes("spark.buffer.pageSize", default)
  }

2. 方法

  这里只列举出有实现逻辑的方法,抽象方法会在下面的类中得到具体实现。
(1)setMemoryStore(store: MemoryStore)
  设置storage内存池中存储对象
(2)releaseExecutionMemory(numBytes: Long, taskAttemptId: Long, memoryMode: MemoryMode)
  根据传入的taskAttemptId 以及numBytes,将对应taskexecution部分内存释放出numBytes
(3)releaseAllExecutionMemoryForTask(taskAttemptId: Long)
  根据传入的taskAttemptId ,将对应的onHeapoffHeapexecution内存池内存全部释放。
(4)releaseStorageMemory(numBytes: Long)
  根据传入的numBytes,将storage内存池的内存释放出numBytes
(5)releaseAllStorageMemory()
  将所有的storage内存池的内存全部释放。
(6)releaseUnrollMemory(numBytes: Long)
  根据传入的numBytes,将unroll内存释放出numBytes。最后也是调用方法(4)。
(7)executionMemoryUsed
  计算onHeapoffHeapExecution内存池的总使用量。
(8)storageMemoryUsed
  计算当前storage内存池的使用量
(9)getExecutionMemoryUsageForTask(taskAttemptId: Long)
  根据传入的taskAttemptId,计算该task使用的onHeapoffHeap内存。

二、StaticMemoryManager

  这个类就是Spark-1.6.0之前版本中主要使用的,对各部分内存静态划分好后便不可变化。

1. 变量

(1)maxUnrollMemory
  由maxStorageMemory(该方法在MemoryManager中被定义)乘以spark.storage.unrollFraction(默认值0.2)来确定。
  也就是说在storage内存中,有一部分会被用于unroll。由于Spark允许序列化和非序列化两种方式存储数据,对于序列化的数据,必须要先展开后才能使用。unroll部分空间就是用于展开序列化数据的。这部分空间是动态分配的

private val maxUnrollMemory: Long = {
    (maxStorageMemory * conf.getDouble("spark.storage.unrollFraction", 0.2)).toLong
  }

2. 方法

(1)getMaxStorageMemory(conf: SparkConf)
  伴生对象中的方法,用于获取storage部分内存大小,计算过程如下:

  private def getMaxStorageMemory(conf: SparkConf): Long = {
    val systemMaxMemory = conf.getLong("spark.testing.memory", Runtime.getRuntime.maxMemory)
    val memoryFraction = conf.getDouble("spark.storage.memoryFraction", 0.6)
    val safetyFraction = conf.getDouble("spark.storage.safetyFraction", 0.9)
    (systemMaxMemory * memoryFraction * safetyFraction).toLong
  }

  systemMaxMemory是当前Executor的内存大小,虽然可以由参数spark.testing.memory来设定,但是这个参数一般用于做测试,在生产上不建议设置。
  memoryFractionstorage内存占整个systemMaxMemory内存的比例,由参数spark.storage.memoryFraction(默认值0.6)来设定。同时为了避免出现OOM的情况,会设定一个安全系数spark.storage.safetyFraction(默认值0.9)。
(2)getMaxExecutionMemory(conf: SparkConf)
  伴生对象中的方法。用于获取execution部分内存大小。计算过程如下:

private def getMaxExecutionMemory(conf: SparkConf): Long = {
    val systemMaxMemory = conf.getLong("spark.testing.memory", Runtime.getRuntime.maxMemory)
    val memoryFraction = conf.getDouble("spark.shuffle.memoryFraction", 0.2)
    val safetyFraction = conf.getDouble("spark.shuffle.safetyFraction", 0.8)
    (systemMaxMemory * memoryFraction * safetyFraction).toLong
  }

  memoryFraction即execution部分占所有能使用内存的百分比,由参数spark.shuffle.memoryFraction(默认值是0.2)来确定。
safetyFractionexecution部分的一个安全阈值,由参数spark.shuffle.safetyFraction(默认值是0.8)来确定。
  总结一下,如果不引入safety的话,整个executor内存的60%用于storage,20%用于execution,剩下20%用于其他。在引入safetyFraction后,默认情况下storage占了整个executor内存的54%,execution占了16%,那么最终还剩下30%内存用于其他用途。注意在storageexecution中的safetyFraction是不一样的,execution部分的safety值更低。
内存使用情况如下图:
StaticMemoryManager内存使用情况
  上图中的Unroll部分,并不是静态分配后不变的,它只是表示Unroll部分的内存最多占了整个storage部分的20%,当storage部分对内存需求比较大时,会使用Unroll部分的内存,当有unroll部分内存申请时,storage部分会释放一些内存以满足unroll部分的申请。unroll部分内存的上限是Storage部分的20%。
(3)acquireExecutionMemory(numBytes: Long, taskAttemptId: Long, memoryMode: MemoryMode)
  申请execution部分内存。根据传入的taskAttemptId,以及需要的内存数numBytes,和当前的MemoryModeON_HEAP还是OFF_HEAP,从对应的execution内存池中申请内存。这里进一步调用ExecutionMemoryPoolacquireMemory方法进行内存的申请。
  ExecutionMemoryPool#acquireMemory方法在最后一部分会介绍到。
(4)acquireStorageMemory(blockId: BlockId, numBytes: Long, evictedBlocks: Buffer)
  申请storage部分内存。在保证申请的内存数numBytes小于maxStorageMemory后,向storage内存池申请numBytes内存。进一步调用StorageMemoryPoolacquireMemory方法进行内存的申请。
  StorageMemoryPool#acquireMemory的执行逻辑在本文最后会有描述。
(5)qcquireUnrollMemory(blockId: BlockId, numBytes: Long, evictedBlocks: Buffer)
  根据传入numBytes,申请unroll部分内存。首先获取当前storage内存池中unroll部分使用的内存数currentUnrollMemory,以及当前storage内存池剩余内存数freeMemory。内存足够时,直接从storage内存池分配numBytes内存。如果内存不足,则会从storage内存池先释放出一部分内存。整个unroll部分使用的内存不能超过maxUnrollMemory

  override def acquireUnrollMemory(
      blockId: BlockId,
      numBytes: Long,
      evictedBlocks: mutable.Buffer[(BlockId, BlockStatus)]): Boolean = synchronized {
    val currentUnrollMemory = storageMemoryPool.memoryStore.currentUnrollMemory
    val freeMemory = storageMemoryPool.memoryFree
    // When unrolling, we will use all of the existing free memory, and, if necessary,
    // some extra space freed from evicting cached blocks. We must place a cap on the
    // amount of memory to be evicted by unrolling, however, otherwise unrolling one
    // big block can blow away the entire cache.
    val maxNumBytesToFree = math.max(0, maxUnrollMemory - currentUnrollMemory - freeMemory)
    // Keep it within the range 0 <= X <= maxNumBytesToFree
    val numBytesToFree = math.max(0, math.min(maxNumBytesToFree, numBytes - freeMemory))
    storageMemoryPool.acquireMemory(blockId, numBytes, numBytesToFree, evictedBlocks)
  }

三、UnifiedMemoryManager

  接下来分析Spark-1.6中引入的动态内存分配概念。
  在UnifiedMemoryManager类注释中写道:

该memoryManager主要是使得execution部分和storage部分的内存不像之前由比例参数限定住,而是两者可以互相借用内存。execution和storage总的内存上限由参数`spark.memory.fraction(默认0.75)来设定的,这个比例是相对于整个JVM heap来说的。
Storage部分可以申请Execution部分的所有空闲内存,直到Execution内存不足时向Storage发出信号为止。当Execution需要更多内存时,Storage部分会向磁盘spill数据,直到把借用的内存都还上为止。
同样的Execution部分也能向Storage部分借用内存,当Storage需要内存时,Execution中的数据不会马上spill到磁盘,因为Execution使用的内存发生在计算过程中,如果数据丢失就会到账task计算失败。Storage部分只能等待Execution部分主动释放占用的内存。

1. 变量

  (1)RESERVED_SYSTEM_MEMORY_BYTES
  伴生对象的一个属性,值为300MB,是Execution和Storage之外的一部分内存,为系统保留。

private val RESERVED_SYSTEM_MEMORY_BYTES = 300 * 1024 * 1024

2. 方法

(1)getMaxMemory(conf: SparkConf)
  伴生对象的方法。获取execution和storage部分能够使用的总内存大小。计算过程如下:

  private def getMaxMemory(conf: SparkConf): Long = {
    val systemMemory = conf.getLong("spark.testing.memory", Runtime.getRuntime.maxMemory)
    val reservedMemory = conf.getLong("spark.testing.reservedMemory",
      if (conf.contains("spark.testing")) 0 else RESERVED_SYSTEM_MEMORY_BYTES)
    val minSystemMemory = reservedMemory * 1.5
    if (systemMemory < minSystemMemory) {
      throw new IllegalArgumentException(s"System memory $systemMemory must " +
        s"be at least $minSystemMemory. Please use a larger heap size.")
    }
    val usableMemory = systemMemory - reservedMemory
    val memoryFraction = conf.getDouble("spark.memory.fraction", 0.75)
    (usableMemory * memoryFraction).toLong
  }

  systemMemory即Executor的内存大小。systemMemory要求最小为reservedMemory的1.5倍,否则直接抛出异常信息。
  reservedMemory是为系统保留的内存大小,可以由参数spark.testing.reservedMemory确定,默认值为上面的300MB。如果为默认值的话,那么对应的会要求systemMemory最小为450MB。
  memoryFraction是整个execution和storage共用的最大内存比例,由参数spark.memory.fraction(默认值0.75)来决定。那么还剩下0.25的内存作为User Memory部分使用。
  那么对一个1GB内存的Executor来说,在默认情况下,可使用的内存大小为(1024 - 300) * 0.75 = 543MB
(2)maxStorageMemory
  storage部分最大内存数。由最大内存数减去ON_HEAP的execution使用的内存大小即可得到。

  override def maxStorageMemory: Long = synchronized {
    maxMemory - onHeapExecutionMemoryPool.memoryUsed
  }

(3)acquireExecutionMemory(numBytes: Long, taskAttemptId: Long, memoryMode: MemoryMode)
  为当前的taskAttemptId申请最多numBytes的内存,如果内存不足则返回0。
  由于这里涉及到的都是Executor JVM Heap中的内存,所以如果是OFF_HEAP模式,直接从offHeapExecution内存池分配。对memoryMode为ON_HEAP的进行如下处理。
 ExecutionMemoryPool#acquireMemory

 onHeapExecutionMemoryPool.acquireMemory(numBytes, taskAttemptId, maybeGrowExecutionPool, computeMaxExecutionPoolSize)

  maybeGrowExecutionPool方法会去释放Storage中保存的数据所占用的内存,收缩Storage部分内存大小,从而增大Execution部分。当Execution部分剩余内存小于numBytes时,执行如下逻辑

val memoryReclaimableFromStorage = math.max(storageMemoryPool.memoryFree, storageMemoryPool.poolSize - storageRegionSize)
  if (memoryReclaimableFromStorage > 0) {
    // Only reclaim as much space as is necessary and available:
    val spaceReclaimed = storageMemoryPool.shrinkPoolToFreeSpace(
    math.min(extraMemoryNeeded, memoryReclaimableFromStorage))
    onHeapExecutionMemoryPool.incrementPoolSize(spaceReclaimed)
  }

如果memoryReclaimableFromStorage大于0,表示storage部分能够分配一些内存给Execution部分,这个值最多不能超过此刻storage内存池的剩余空闲内存。然后取出spaceReclaimed的内存给Execution部分,实时调整Storage和Execution内存池的大小。
  在内存区域调整后,会重新计算当前Execution内存池大小computeMaxExecutionPoolSize。然后调用ExecutionMemoryPool#acquireMemory方法向Execution内存池申请内存。该方法在本文最后会有描述。
(4)acquireStorageMemory(blockId: BlockId, numBytes: Long, evictedBlocks: Buffer)
  首先申请的storage内存numBytes不能超过storage部分内存的最大值maxStorageMemory。
  然后当storage部分内存不足以满足此次申请时,尝试向execution内存池借用内存,借到的内存大小为min(execution内存池剩余内存,numBytes),并且实时调整execution和storage内存池的大小,如下面的代码所描述的。

if (numBytes > storageMemoryPool.memoryFree) {
      // There is not enough free memory in the storage pool, so try to borrow free memory from
      // the execution pool.
      val memoryBorrowedFromExecution = Math.min(onHeapExecutionMemoryPool.memoryFree, numBytes)
      onHeapExecutionMemoryPool.decrementPoolSize(memoryBorrowedFromExecution)
      storageMemoryPool.incrementPoolSize(memoryBorrowedFromExecution)
    }

  最后,向storageMemoryPool申请numBytes的内存。这一部分逻辑在本文最后StorageMemoryPool#acquireMemory中会有详细描述。
(5)acquireUnrollMemory
  直接调用方法(4)从storage部分申请内存。

  UnifiedMemoryManager的内存分配情况如下图所示:
  UnifiedMemoryManager内存管理
  该图片参考自:https://0x0fff.com/spark-memory-management/#comment-1188

四、ExecutionMemoryPool和StorageMemoryPool

1、ExecutionMemoryPool

(1)memoryForTask变量
  这个变量的定义如下,是一个HashMap结构,用于存储每个Task所使用的Execution内存情况,key为taskAttemptId, value为使用的内存数。

private val memoryForTask = new mutable.HashMap[Long, Long]()

(1)acquireMemory(numBytes: Long, taskAttemptId: Long, maybeGrowPool: Long, computeMaxPoolSize: Long)方法
  在该方法中主要有一个循环:

while (true) {
      val numActiveTasks = memoryForTask.keys.size
      val curMem = memoryForTask(taskAttemptId)

      maybeGrowPool(numBytes - memoryFree)

      val maxPoolSize = computeMaxPoolSize()
      val maxMemoryPerTask = maxPoolSize / numActiveTasks
      val minMemoryPerTask = poolSize / (2 * numActiveTasks)

      val maxToGrant = math.min(numBytes, math.max(0, maxMemoryPerTask - curMem))
      val toGrant = math.min(maxToGrant, memoryFree)

      if (toGrant < numBytes && curMem + toGrant < minMemoryPerTask) {
        logInfo(s"TID $taskAttemptId waiting for at least 1/2N of $poolName pool to be free")
        lock.wait()
      } else {
        memoryForTask(taskAttemptId) += toGrant
        return toGrant
      }
    }

  程序一直处理该task的请求,直到系统判定无法满足该请求或者已经为该请求分配到足够的内存为止。如果当前execution内存池剩余内存不足以满足此次请求时,会向storage部分请求释放出被借走的内存以满足此次请求。
  根据此刻execution内存池的总大小maxPoolSize,以及从memoryForTask中统计出的处于active状态的task的个数计算出每个task能够得到的最大内存数maxMemoryPerTask = maxPoolSize / numActiveTasks。每个task能够得到的最少内存数minMemoryPerTask = maxMemoryPerTask / 2。
  根据申请内存的task当前使用的execution内存大小决定分配给该task多少内存,总的内存不能超过maxMemoryPerTask。但是如果execution内存池能够分配的最大内存小于numBytes并且如果把能够分配的内存分配给当前task,但是该task最终得到的execution内存还是小于minMemoryPerTask时,该task进入等待状态,等其他task申请内存时将其唤醒。如果满足内存分配统计,就会返回能够分配的内存数,并且更新memoryForTask,将该task使用的内存调整为分配后的值。一个Task最少需要minMemoryPerTask才能开始执行。

2、StorageMemoryPool

(1)acquireMemory(blockId: BlockId, numBytesToAcquire: Long, numBytesToFree: Long, evictedBlocks: Buffer)
  numBytesToAcquire是申请内存的task传入的numBytes参数。

val numBytesToFree = math.max(0, numBytes - memoryFree)

numBytesToFree表示storage空闲内存与申请内存的差值,需要storage释放numBytesToFree的内存才能满足numBytes的申请。
  该方法的主要逻辑在下面这段代码中:

if (numBytesToFree > 0) {
      memoryStore.evictBlocksToFreeSpace(Some(blockId), numBytesToFree, evictedBlocks)
      // Register evicted blocks, if any, with the active task metrics
      Option(TaskContext.get()).foreach { tc =>
        val metrics = tc.taskMetrics()
        val lastUpdatedBlocks = metrics.updatedBlocks.getOrElse(Seq[(BlockId, BlockStatus)]())
        metrics.updatedBlocks = Some(lastUpdatedBlocks ++ evictedBlocks.toSeq)
      }
    }

  在申请内存时,如果numBytes大于此刻storage内存池的剩余内存,则需要storage内存池释放一部分内存以满足申请需求。释放内存后如果memoryFree >= numBytes,就会把这部分内存分配给申请内存的task,并且更新storage内存池的使用情况。
  释放内存部分的逻辑,调用MemoryStore#evictBlockToFreeSpace,在MemoryStore中有一个entries对象,它是一个LinkedHashMap结构,key为BlociId,value为记录了内存使用情况的一个对象。循环从最开始计算entries中每个Bokc使用的storage内存大小,取出一个就累加一下,直到累加内存大小达到前面的请求值numBytes,然后把这些BlockId对应的数据通过BlockManager充内存中直接清除,调用BlockManager#dropFromMemory把数据spill到磁盘上。

转载于:https://www.cnblogs.com/wuyida/p/6300247.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spark内存管理-UnifiedMemoryManager和StaticMemoryManager 的相关文章

  • AI在保护环境、应对气候变化中的作用

    对于AI生命周期数据领域的全球领导者而言 暂时搁置我们惯常的AI见解和AI生命周期数据内容产出 来认识诸如世界地球日这样的自然环境类活动日 似乎是个奇怪的事情 我们想要知道 数据是否真的会影响我们的地球环境 简而言之 是 确实如此 但作为一
  • 是否可以在 C++ 运行时动态创建函数?

    C 是一种静态的编译语言 模板在编译时解析等等 但是是否有可能在运行时创建一个函数 该函数未在源代码中描述 并且在编译期间未转换为机器语言 以便用户可以向其抛出源代码中未预期的数据 我知道这不可能以直接的方式发生 但肯定是可能的 有很多编程
  • 实力认证!鼎捷软件荣膺“领军企业”和“创新产品”两大奖项

    近日 由中国科学院软件研究所 中科软科技股份有限公司联合主办的 2023中国软件技术大会 于北京成功举办 本届大会以 大模型驱动下的软件变革 为主题 数十位来自知名互联网公司和软件巨头企业的技术大咖 不同领域行业专家 畅销书作者等分享嘉宾
  • 混合运行时是可行的解决方案吗?

    在我的公司 我们最近从 VC9 切换到 VC10 我们迁移了我们的项目 但是负责人告诉我们 我们必须在我们的生产机器上保留一些用 VC9 编译的基本通用 DLL 一段时间 这些 DLL 使用自定义结构 其中一些包含std vector st
  • 查找关联程序以使用 Java 打开文件

    我希望使用计算机上安装的关联程序 在本例中使用 MS Word 或 Open Office Writer 从 Java 应用程序打开文件 比如说 word 文档 问题是我想等到这个子进程完成 这可以使用 Process 类中的 waitFo
  • Microsoft Visual C++ 运行时版本?我需要把它们全部保留吗?

    我环顾四周 不确定是否能找到明确的答案 所以如果之前有人问过这个问题 我很抱歉 我的 Google Fu 让我失望了 我们正在针对 NET Framework 开发软件 但从历史上看 我们必须包含许多第三方组件 此外 从历史上看 这些往往与
  • 将大文件作为流发送到 process.getOutputStream

    我在 Windows 机器中使用 gzip 实用程序 我压缩了一个文件并作为 blob 存储在数据库中 当我想使用 gzip 实用程序解压缩此文件时 我将此字节流写入 process getOutputStream 但超过30KB后 就无法
  • C、运行时测试 PATH 中是否存在可执行文件

    我目前正在用 C 语言编写一个应用程序 目标是 BSD 和 Linux 系统 希望能够普遍移植 该程序具有运行时依赖项 在本例中为 mplayer 就目前情况而言 我正在使用execlp 启动 mplayer 我正在检查 execlp 调用
  • 如何禁用java中的运行时警告?

    我在 java 程序中使用 jar 文件 它在运行时生成警告 但我不希望我的客户看到这些警告 我怎样才能禁用这些警告 警告如下 Sep 25 2009 10 10 33 PM com gargoylesoftware htmlunit In
  • 什么时候插入排序比合并排序快?

    对于家庭作业问题 我被告知插入排序以 8n 2 运行 合并排序以 64 n lg n 运行 作为我得到的解决方案的一部分 它说只要 n 它来自这个 代数 推理路线 steps in insertion sort lt steps in me
  • Android 运行时布局教程

    有谁知道如何在 android 运行时执行活动布局或有一个很好的参考 这是我的活动的代码 我确信我只是忽略了在这里做一些事情 package com isi sa import android app Activity import and
  • 为什么 UIView 的框架没有在 ViewDidLayoutSubviews 中更新?

    我正在尝试更新一个框架UIView其中包含按钮和标签 我正在尝试更新它viewDidLayoutSubviews 我也尝试过viewDidLoad viewWillAppear viewDidAppear 我想更改视图的 y 位置 orig
  • 发送 cmdarray 供 exec 处理——hello world

    我不是发送一系列命令 hello world 到exec https docs oracle com javase 7 docs api java lang Runtime html exec 28java lang String 5B 5
  • 在 Java Runtime.getRuntime().exec(...) 中使用引号和双引号

    我正在尝试在 Mac OSX 中从 Java 启动 Lisp 映像 使用控制台中的图像 我输入以下内容 lisp image eval package method some argument 一切都运行良好 在Java中 我在使用传递引号
  • 如何加载使用 VaryByControl OutputCache 的控件,并指定属性值

    我有一个应该使用缓存的用户控件 其中VaryByControl The ascx文件看起来像这样 p Nothing p The TestControl代码隐藏文件中的类有一个int Test 财产和Page Load 填充的事件处理程序S
  • 未确定的泛型类型在 ghci 的运行时中如何表示

    我很清楚通用函数和通用数据类型 在泛型类型中 data SB forall x show x gt SB x instance Show SB where show SB x show x 所以对于任何给定类型x 如果它有一个签名Show
  • 运行时 SQL 查询生成器

    我的问题类似于 Java中有什么好的动态SQL生成器库吗 https stackoverflow com questions 5620985 is there any good dynamic sql builder library in
  • Java - Runtime.getRuntime().exec() 发生了什么?

    我在 Java 中遇到 Runtime exec 问题 我的代码 String lol home pc example txt String b touch lol try Runtime getRuntime exec b catch E
  • GOMAXPROCS 默认值是多少?

    不设置同名环境变量时是否保证GOMAXPROCS设置为1 此代码显示的值 package main import runtime fmt func getGOMAXPROCS int return runtime GOMAXPROCS 0
  • 确定 Objective-C 方法在运行时是否是可变的

    有没有办法在运行时找出给定方法是否是可变参数类型 就像是method getTypeEncoding 这不会告诉我一个方法是否接受可变数量的参数 或者有什么技巧可以告诉我们吗 罗伯特的评论是正确的 考虑 interface Boogity

随机推荐

  • [洛谷] [NOIP2018 提高组] 旅行 加强版 - 基环树

    题目链接 https www luogu com cn problem P5049 题目描述 小 Y 是一个爱好旅行的 OIer 她来到 X 国 打算将各个城市都玩一遍 小Y了解到 X国的 n 个城市之间有 m 条双向道路 每条双向道路连接
  • d3dcompiler_43.dll缺失怎么修复方法_d3dcompiler43dll丢失怎么解决

    懂电脑的人都知道 dll文件是电脑运行各种程序的根本 少了它的话无论什么软件 游戏都运行不了 但是dll文件又并不只有一种 其中最常丢失的是这款d3dcompiler 43 dll文件 这款文件是运行电脑系统的关键 也是运行电脑上常用程序的
  • Go 语法 变量

    文章目录 简介 一些语法 go 数据类型 demo code 简介 go的一个思想 一个问题尽量只有一个解决方案是最好的 go 中函数是第一等元素 studygolang com pkgdoc go build src go 编译 go r
  • Vulkan同步机制和图形-计算-图形转换的风险(一)

    在现代渲染环境中 很多情况下在一个数据帧期间会产生计算负荷 在GPU上计算通常 非固定功能 是并行编程的 通常用于具有挑战性 完全不可能或仅通过标准图形管道 顶点 几何 细化 栅格 碎片 实现的效率低下的技术 一般情况下 计算在实现技术方面
  • scrollIntoView() 方法实现元素滚动

    TOC scrollIntoView 方法实现元素滚动 element scrollIntoView Element 接口 dom元素 的 scrollIntoView 方法会滚动元素的父容器 使被调用 scrollIntoView 的元素
  • python更多语法

    本文译自https docs python org 2 7 tutorial 完全是出于个人兴趣翻译的 请勿追究责任 另外 谢绝商业牟利 刊印请与本人和原作者联系 无授权不得刊印 违者必究其责任 如需转发 请注明来源 并保留此行 尊重本人的
  • Mac环境下配置JAVA_HOME

    Mac环境下配置JAVA HOME 1 下载JDK版本 JDK官方下载地址 Java下载地址 下载旧版本需要注册oracle用户 下载jdk 12 0 2 osx x64 bin dmg并点击安装 嫌浏览器下载慢的可以把下载地址粘贴到迅雷中
  • Tomcat配置HTTPS访问

    在tomcat中存在两种证书验证情况 1 单向验证 2 双向验证 1 tomcat单向认证 服务器端会提供一个公开的公钥 每一个访问此服务器的客户端都可以获得这个公钥 此公钥被加密后 服务器端可以进行解密处理 之后验证是否配对 配置 在此次
  • postgresql使用UUID函数gen_random_uuid()

    PostgreSQL 13版本前不提供生成UUID数据的内置函数 如果需要使用UUID数据 可通过创建外部扩展 uuid ossp或 pgcrypto生成 UUID数据 PostgreSQL 13 新增gen random uuid 内置函
  • 下载JDK并配置JDK环境变量

    1 下载JDK8或者JDK11 1 点击链接进入官网下载页面 https www oracle com java technologies downloads java8 下拉页面一直到下载的地方 在这里可以选择是下载JDK8还是JDK11
  • 判断某个数组是否包含在另一个数组中

    判断b数组是否包含在a数组中 function isContained a b if a instanceof Array b instanceof Array return false if a length lt b length re
  • 打开cmd闪退

    我们在使用电脑过程中一般会很少用到cmd命令 CMD命令窗口在一些特殊情况时我们会用到 如PING下看网络通不通 在CMD窗口里运行命令如磁盘格式转换 但是有些朋友遇到了这样的问题 在开始运行输入CMD回车后 CMD命令黑框框出来闪一下就消
  • 使用“VMware ThinApp”绿化软件

    当我看到 WPS Office 在我的电脑中写入了上万条注册表项时 我几乎要崩溃了 这个 有点太多了吧 软件绿化工具 环境 Workstation 15 5 Player for Windows 绿化软件 VMware ThinApp 软件
  • 纹波电压

    什么是纹波电压 狭义上的纹波电压 是指输出直流电压中含有的工频交流成分 直流电压本来应该是一个固定的值 但是很多时候它是通过交流电压整流 滤波后得来的 由于滤波不彻底 就会有剩余的交流成分 下图为1 8V的纹波电压 纹波电压是如何产生的 首
  • 2022年Python笔试选择题及答案(秋招)

    2022年Python笔试选择题及答案 秋招 单选题 1 以下关于 Python 的描述错误的是 A Python 的语法类似 PHP B Python 可用于 Web 开发 C Python 是跨平台的 D Python 可用于数据抓取
  • 网络编程day7作业

    将词典导入数据库 include
  • Unity 空格会触发Button的问题

    问题 做了一个界面 空格可以召唤或者关闭一个面板 上面有Button可以控制人物数量信息 当点击过Button修改数量 再按下空格隐藏面板后 最后点击的Button就会被空格键触发一次 如下图 解决 这是由于Button中Navigatio
  • PAT乙级题解—— 1071 小赌怡情 (15分)

    常言道 小赌怡情 这是一个很简单的小游戏 首先由计算机给出第一个整数 然后玩家下注赌第二个整数将会比第一个数大还是小 玩家下注 t 个筹码后 计算机给出第二个数 若玩家猜对了 则系统奖励玩家 t 个筹码 否则扣除玩家 t 个筹码 注意 玩家
  • vue 组件生命周期钩子详解

    Vue是一个自带组件系统的前端框架 Vue的每一个实例其实就是一个组件 我们在组织我们的页面结构的时候其实就是在定一个一个组件 然后拼装在一起 完成一个复杂的页面逻辑 组件主要包含 数据 模版 以及链接数据和模版的状态响应系统 除了这些 我
  • Spark内存管理-UnifiedMemoryManager和StaticMemoryManager

    在Spark 1 6 0中 引入了一个新的参数spark memory userLegacyMode 默认值为false 表示不使用Spark 1 6 0之前的内存管理机制 而是使用1 6 0中引入的动态内存分配这一概念 从SparkEnv