Java垃圾收集蒸馏

串行,并行,并发,CMS,G1,Young Gen,New Gen,Old Gen,Perm Gen,Eden,Tenured,Survivor Spaces,Safepoints和数百个JVM启动标志。 在尝试调优垃圾收集器并尝试从Java应用程序获取所需的吞吐量和延迟时,这是否使您感到困惑? 如果确实如此,那就不用担心,您并不孤单。 描述垃圾收集的文档就像飞机的手册页。 每个旋钮和转盘都有详细的说明,但是找不到任何关于如何飞行的指南。 本文将尝试解释为特定工作负载选择和调整垃圾收集算法时的权衡取舍。

重点将放在Oracle Hotspot JVM和OpenJDK收集器上,因为它们是最常用的收集器。 最后,将讨论其他商用JVM以说明替代方案。

权衡

明智的人们不断告诉我们: “您一无所获” 。 当我们得到一些东西时,我们通常不得不放弃一些回报。 当涉及到垃圾收集时,我们会使用3个主要变量来设置收集器的目标:

  1. 吞吐量:应用程序完成的工作量与在GC中花费的时间之比。 ‑XX:GCTimeRatio = 99的目标吞吐量; 默认值为99,等于1%的GC时间。
  2. 延迟:系统响应事件所花费的时间,该时间受垃圾收集引入的暂停影响。 GC的目标延迟时间为‑XX:MaxGCPauseMillis = <n>。
  3. 内存:我们的系统用于存储状态的内存量,在管理状态时通常会对其进行复制和移动。 应用程序在任何时间点保留的活动对象集称为实时集。 最大堆大小–Xmx <n>是用于设置应用程序可用堆大小的调整参数。

注意: Hotspot通常无法实现这些目标,并且会在没有任何警告的情况下默默地继续运行,因为它已经大大偏离了目标。

延迟是事件之间的分布。 增加平均等待时间以减少最坏情况的等待时间或降低等待时间可能是可以接受的。 我们不应将“实时”一词解释为意味着最低的延迟。 实时是指无论吞吐量如何都具有确定性的延迟。

对于某些应用程序工作负载,吞吐量是最重要的目标。 一个示例将是长期运行的批处理作业。 只要可以更快地完成整个作业,那么在进行垃圾收集时是否偶尔将批处理作业暂停几秒钟并不重要。

对于几乎所有其他工作负载,从面向人类的交互式应用程序到金融交易系统,如果系统在某些情况下无法响应的时间超过几秒钟甚至几毫秒,则可能会带来灾难。 在金融交易中,通常值得牺牲一些吞吐量以换取一致的延迟。 我们可能还会有一些应用程序,这些应用程序受到可用物理内存量的限制,并且必须保持占用空间,在这种情况下,我们必须放弃延迟和吞吐量方面的性能。

权衡通常表现如下:

  • 通过为垃圾回收算法提供更多的内存,可以在很大程度上减少作为摊销成本的垃圾回收成本。
  • 通过包含活动集并保持堆大小较小,可以减少由于垃圾收集而导致的观察到的最坏情况的延迟引起的暂停。
  • 通过管理堆和世代大小以及控制应用程序的对象分配速率,可以减少出现暂停的频率。
  • 通过与应用程序同时运行GC,可以减少较大的暂停频率,有时会牺牲吞吐量。

对象寿命

垃圾收集算法通常经过优化,以期大多数对象的生存时间很短,而很少有对象生存时间很长。 在大多数应用程序中,生存期很长的对象往往构成随时间分配的对象的很小一部分。 在垃圾收集理论中,这种观察到的行为通常被称为“ 婴儿死亡率 ”或“ 弱代假设 ”。 例如,循环迭代器通常寿命很短,而静态字符串实际上是永生的。

实验表明,世代垃圾收集器通常可以比非世代收集器支持更大数量级的吞吐量,因此几乎在服务器JVM中广泛使用。 通过分离对象的世代,我们知道新分配对象的区域对于活动对象可能非常稀疏。 因此,收集器在此新区域中清除少量活动对象并将其复制到较旧对象的另一个区域中可以非常有效。 热点垃圾收集器根据生存的GC周期数记录对象的年龄。

注意:如果您的应用程序持续生成许多可以生存很长时间的对象,则可以预期您的应用程序将花费大量的时间进行垃圾回收,并希望花费大量的时间来调整Hotspot垃圾收集器。 这是由于世代“过滤器”效率降低时发生的GC效率降低,以及导致更频繁地收集更长寿命的世代的成本。 老一辈人稀少,因此老一辈人收集算法的效率往往要低得多。 分代垃圾收集器通常以两个不同的收集周期运行:收集短期对象的次要垃圾收集,收集较旧区域的次要垃圾收集。

世界停止活动

应用程序在垃圾回收期间遭受的暂停是由于所谓的世界停止事件造成的。 为了使垃圾收集器运行,出于实际工程上的原因,有必要定期停止正在运行的应用程序,以便可以管理内存。 根据算法的不同,不同的收集器将在特定的执行点停下世界,并持续不同的时间。 要使应用程序完全停止,必须暂停所有正在运行的线程。 垃圾收集器通过发信号通知线程在到达“ 安全点 ”时停止运行来做到这一点,这是程序执行过程中所有GC根已知且所有堆对象内容一致的点。 根据线程在做什么,可能需要一些时间才能达到安全点。 安全点检查通常在方法返回和回送边沿上执行,但可以在某些地方进行优化,从而使其在动态上更加罕见。 例如,如果线程正在复制大型数组,克隆大型对象或执行具有有限界限的单调计数循环,则到达安全点可能要花费几毫秒的时间。 安全时间(TTS)是低延迟应用程序中的重要考虑因素。 通过启用‑XX:+ PrintGCApplicationStoppedTime标志以及其他GC标志,可以浮现此时间。

注意:对于具有大量正在运行的线程的应用程序,当世界停止事件发生时,随着线程在释放后恢复,系统将承受重大的调度压力。 因此,较少依赖于世界停止事件的算法可能会更有效。

热点堆组织

要了解不同收集器的工作方式,最好是探索如何组织Java堆来支持分代收集器。

伊甸园是最初分配大多数对象的区域。 幸存者空间是临时存储对象,这些对象在伊甸园空间中幸存下来。 讨论次要收藏时,将描述幸存者空间的使用情况。 伊甸园和幸存者空间统称为“年轻”新生代”。

寿命足够长的对象最终将提升为使用期限

烫发生成是运行时将其“知道”为有效的对象(例如类和静态字符串)存储的地方。 不幸的是,在许多应用程序中持续使用类加载的常见用法使烫发生成背后的动机假设错误,即类是不朽的。 在Java 7中,已将字符串从permgen转移到Tenured ,而从Java 8开始,不再存在perm生成,因此本文将不进行讨论。 大多数其他商业收藏家并不使用单独的烫发空间,而是倾向于将所有长期存在的物品视为永久使用。

注意:通过虚拟空间,收集器可以调整区域的大小,以满足吞吐量和延迟目标。 收集器会保留每个收集阶段的统计信息,并相应地调整区域大小,以达到目标。

对象分配

为了避免争用,每个线程都分配有一个线程本地分配缓冲区(TLAB),从该线程中分配对象。 使用TLAB可以避免在单个内存资源上发生争用,从而使对象分配随线程数扩展。 通过TLAB分配对象是非常便宜的操作; 它只是为对象大小增加一个指针,在大多数平台上大约需要10条指令。 Java的堆内存分配甚至比C运行时中的malloc便宜。

注意:尽管单个对象分配非常便宜,但必须进行次要收集的速率与对象分配速率成正比。

当TLAB耗尽时,一个线程只需向Eden空间请求一个新线程。 当伊甸园装满后,便开始小规模收集。

大对象(-XX:PretenureSizeThreshold = <n>)可能无法容纳在年轻一代中,因此必须在旧一代中进行分配,例如大型数组。 如果将阈值设置为低于TLAB大小,则不会在旧版本中创建适合TLAB的对象。 新的G1收集器以不同的方式处理大型对象,稍后将在其单独的部分中进行讨论。

小型收藏

当伊甸园变满时,将触发次要回收。 这是通过将新一代的所有活动对象适当地复制到幸存者空间或保有权空间来完成的。 复制到使用权空间称为升级或使用权。 对于足够旧的对象(– XX:MaxTenuringThreshold = <n>),或幸存者空间溢出时,将进行升级。

活动对象是应用程序可访问的对象。 任何其他物体均无法到达,因此可以视为已死亡。 在次要集合中,首先通过遵循所谓的GC根目录执行活动对象的复制,然后反复复制可到达生存空间的任何对象。 GC根通常包括来自应用程序和JVM内部静态字段以及线程堆栈框架的引用,所有这些引用均有效指向应用程序的可访问对象图。

在世代集合中,新一代可访问对象图的GC根目录还包括从旧一代到新一代的所有引用。 还必须对这些引用进行处理,以确保新一代中的所有可访问对象在次要集合中都不会丢失。 通过使用“ 卡片表 ”来识别这些跨代参考。 热点卡表是一个字节数组,其中每个字节用于跟踪旧一代的相应512字节区域中跨代引用的潜在存在。 在将引用存储到堆时,“存储屏障”代码将标记卡,以指示关联的512字节堆区域中可能存在从旧一代到新一代的潜在引用。 在收集时,卡表用于扫描此类跨代引用,这些引用有效地代表了新一代的其他GC根。 因此,次要收藏的重大固定成本与上一代的大小成正比。

新一代Hotspot中有两个幸存者空间,它们在“ 到太空 ”和“ 从太空 ”角色中交替出现。 在次要收集开始时,到太空幸存者空间始终是空的,并充当次要收集的目标复制区域。 先前的次要收藏的目标幸存者空间是起始空间的一部分,起始空间还包括伊甸园,在伊甸园中可以找到需要复制的活动对象。

少量GC收集的成本通常由将对象复制到幸存者和保有权空间的成本决定。 无法从次要收藏中幸存下来的对象可以有效地自由处理。 在次要收藏期间完成的工作与发现的活动对象的数量成正比,而不与新一代的大小成正比。 每次将伊甸园面积扩大一倍时,花在次要收藏上的总时间几乎可以减少一半。 因此可以将内存用于吞吐量。 将Eden大小增加一倍会导致每个收集周期的收集时间增加,但是如果要提升的对象数和旧一代的大小都恒定,则这相对较小。

注意:在热点中,次要收藏是世界停止事件。 随着越来越多的活动对象堆越来越大,这正Swift成为一个主要问题。 我们已经开始看到需要同时收集年轻一代以达到暂停时间目标的需求。

主要收藏

主要藏品收集了一代,以便可以从年轻一代中推广物品。 在大多数应用程序中,绝大多数程序状态最终出现在老一代。 对于前代来说,存在种类最多的GC算法。 有些会在填满时压缩整个空间,而另一些会与应用程序同时收集以防止填满。

老一代的收藏家将尝试预测何时需要收藏,以避免年轻一代的晋升失败。 收集器跟踪旧一代的填充阈值,并在超过该阈值时开始收集。 如果此阈值不足以满足促销要求,那么将触发“ FullGC ”。 FullGC涉及推广年轻一代的所有活物,然后收集和压缩旧一代。 升级失败是一项非常昂贵的操作,因为必须解开此循环中的状态和升级对象,以便发生FullGC事件。

注意:为避免升级失败,您将需要调整旧版本允许容纳升级的填充(‑XX:PromotedPadding = <n>)。

注意:当堆需要增长时,会触发FullGC。 通过将–Xms和–Xmx设置为相同的值,可以避免这些堆大小调整的FullGC。

除FullGC之外,老一代的压缩很可能是应用程序将遇到的最大的停顿停顿状态。 压缩的时间往往会随着使用权空间中活动对象的数量线性增长。

有时,可以通过增加幸存者空间的大小和对象的年龄来降低占位空间的填充率,然后提升为占位空间。 但是,在促销之前增加次要收藏中幸存者空间的大小和对象年龄(–XX:MaxTenuringThreshold = <n>)也会增加次要收藏物中的成本和暂停时间,这是由于未成年收藏者之间的生存空间之间的复制成本增加了集合。

串行收集器

串行收集器(-XX:+ UseSerialGC)是最简单的收集器,是单处理器系统的不错选择。 它还具有所有收集器中最小的占地面积。 它对次要和主要集合都使用一个线程。 使用简单的凹凸指针算法在持久空间中分配对象。 当使用权空间已满时,将触发主要集合。

并联收集器

并行收集器有两种形式。 并行收集器 (‑XX:+ UseParallelGC),它使用多个线程来执行年轻代的次要收集,并使用单个线程来执行旧代的主要收集。 自Java 7u4起默认的Parallel Old收集器 (‑XX:+ UseParallelOldGC)使用多个线程进行次要收集,并使用多个线程进行主要收集。 使用简单的凹凸指针算法在持久空间中分配对象。 当使用权空间已满时,将触发主要集合。

在多处理器系统上,并行旧收集器将提供所有收集器中最大的吞吐量。 直到发生收集为止,它对正在运行的应用程序没有影响,然后将使用最有效的算法使用多个线程并行收集。 这使得Parallel Old Collector非常适合批处理应用。

收集旧版本的成本受要保留的对象数量的影响大于受堆大小影响的对象。 因此,可以通过提供更多内存并接受更大但更少的收集暂停来提高Parallel Old收集器的效率,以实现更大的吞吐量。

期望使用此收集器获得最快的次要收集,因为升级到保有空间仅是指针和复制操作的简单颠簸。

对于服务器应用程序,Parallel Old收集器应该是第一个调用端口。 但是,如果主要的收集暂停时间超出了您的应用程序所能承受的范围,则您需要考虑使用并发收集器,该并发收集器在应用程序运行时并发收集长期对象。

注意:在压缩旧版本的同时,现代硬件上每GB实时数据的暂停时间大约为1-5秒。

注意:通过为CPU套接字本地的线程分配Eden内存,并行收集器有时可以在多路CPU服务器应用程序上从-XX:+ UseNUMA获得性能优势。 可惜的是,该功能对其他收集器不可用。

并发标记扫描(CMS)收集器

CMS(-XX:+ UseConcMarkSweepGC)收集器在旧版本中运行,以收集在大型收集期间不再可访问的终身对象。 它与应用程序同时运行,目的是在老一代中保留足够的可用空间,从而不会发生年轻一代的升级失败。

升级失败将触发FullGC。 CMS遵循多个步骤:

  1. 初始标记 :查找GC根。
  2. 并发标记 :标记GC根目录中的所有可到达对象。
  3. 并发预清理 :通过标记检查在并发标记阶段是否已更新对象引用和已提升对象。
  4. 备注 :捕获自预清理阶段以来已更新的对象引用。
  5. 并发扫描 :通过回收死对象占用的内存来更新空闲列表。
  6. 并发重置 :重置数据结构以供下次运行。

当租用对象变得不可访问时,CMS将回收该空间并将其放入空闲列表。 进行促销时,必须在自由列表中搜索适合促销对象尺寸的Kong。 与Parallel Collector相比,这增加了推广成本,从而增加了Minor收藏的成本。

注意 :CMS不是压缩收集器,随着时间的推移,它可能导致旧的碎片化。 对象升级可能会失败,因为大型对象可能不适合旧版本中的可用Kong。 发生这种情况时,会记录“ 升级失败 ”消息,并触发FullGC压缩活动的使用权对象。 对于此类压缩驱动的FullGC,由于CMS仅使用单个线程进行压缩,因此期望的延迟比使用Parallel Old收集器的主要收集更糟糕。

CMS通常与应用程序并发,这具有许多含义。 首先,CPU时间由收集器占用,从而减少了可用于应用程序的CPU。 CMS所需的时间与将对象提升到保有空间的数量成线性增长。 其次,对于并发GC周期的某些阶段,必须将所有应用程序线程带入一个安全点,以标记GC根并执行并行重新标记以检查变异。

注意 :如果应用程序发现使用权对象发生了重大变化,则重新标记阶段可能很重要,在极端情况下,重新标记阶段可能比使用Parallel Old Collector进行完全压缩要花费更长的时间。

CMS使FullGC成为不太频繁的事件,但代价是吞吐量降低,更昂贵的次要收集和更大的占用空间。 与并行收集器相比,吞吐量的下降幅度可能在10%-40%之间,具体取决于提升率。 CMS还需要占用20%的空间,以容纳其他数据结构和“浮动垃圾”,这些并发标记在传递到下一个周期的并发标记期间可能会丢失。

有时可以通过增加年轻一代空间和老一代空间的大小来降低高晋升率和由此产生的碎片。

注意 :如果CMS收集速度不足以跟上升级的速度,则CMS可能会遇到“ 并发模式故障 ”,这可以在日志中看到。 当收集开始得太晚时可能导致这种情况,有时可以通过调整来解决。 但是,当收集率无法跟上某些应用程序的高推广率或高对象突变率时,也会发生这种情况。 如果应用程序的提升率或变异率太高,则您的应用程序可能需要进行一些更改以减轻提升压力。 向这样的系统添加更多的内存有时会使情况变得更糟,因为CMS将需要更多的内存来进行扫描。

垃圾优先(G1)收集器

G1(-XX:+ UseG1GC)是Java 6中引入的新收集器,现已从Java 7u4开始正式支持。 这是一种部分并发的收集算法,该算法还尝试在较小的增量“停止世界”停顿中压缩占位空间,以尽量减少由于碎片而困扰CMS的FullGC事件。 G1是一个世代收集器,通过将其划分为大量(〜2000个)可变目的的固定大小区域(而不是出于相同目的的连续区域)来与其他收集器进行不同的组织。

G1采用同时标记区域的方法来跟踪区域之间的引用,并将集合集中在具有最大可用空间的区域上。 然后,通过将活动对象疏散到一个空的区域,以停下来的停顿增量收集这些区域,从而在此过程中进行压缩。 一个循环中要收集的区域称为收集集

大于某个区域50%的对象分配在多个区域中的大型区域中。 在G1下,大型对象的分配和收集可能会非常昂贵,并且迄今为止几乎没有或没有进行任何优化。

任何压缩收集器的挑战不是对象的移动,而是对那些对象的引用的更新。 如果从许多区域引用了一个对象,则更新这些引用所花费的时间可能比移动该对象要长得多。 G1通过“ 记住的集合 ”跟踪区域中哪些对象具有其他区域的引用。 记住集是标记为突变的牌的集合。 如果“记住的集合”变大,则G1会显着降低速度。 当将对象从一个区域撤离到另一个区域时,相关的世界停止事件的长度往往与具有需要扫描和潜在修补的参考的区域数量成比例。

维护“记住的集”会增加次要收集的成本,从而导致暂停时间要比“平行老”或CMS的次要收集器更长。

G1是目标驱动程序,其时延为–XX:MaxGCPauseMillis = <n>,默认值= 200ms。 该目标将尽力而为地影响每个周期的工作量。 在几十毫秒内设置目标通常是徒劳的,而在撰写本文时,针对数十毫秒的目标还不是G1的重点。

对于较大的堆,G1是一个很好的通用收集器,当应用程序可以容忍0.5-1.0秒范围内的增量压缩暂停时,G1往往会变得碎片化。 G1倾向于减少CMS看到的最坏情况的停顿频率,这是因为碎片化的代价是扩展了次要收藏集并增加了旧一代的压缩能力。 大多数暂停最终都局限于区域压缩,而不是全部堆压缩。

像CMS一样,G1也可能无法跟上晋升率,并且会退回到世界末日的FullGC。 就像CMS具有“ 并发模式故障 ”一样,G1可能会发生疏散故障,在日志中被视为“ 空间溢出 ”。 当没有空闲区域可将对象撤离时,就会发生这种情况,这类似于升级失败。 如果发生这种情况,请尝试使用更大的堆和更多的标记线程,但是在某些情况下,可能需要更改应用程序以降低分配率。

对于G1来说,一个具有挑战性的问题是处理受欢迎的物体和区域。 当区域中的活动对象未从其他区域大量引用时,逐步停止压缩将非常有效。 如果某个对象或区域很受欢迎,则记住的集合会很大,G1将尝试避免收集这些对象。 最终,它别无选择,这会导致在堆压缩时非常频繁的中长度暂停。

替代并行收集器

CMS和G1通常被称为并发收集器。 当您查看所执行的全部工作时,很显然,年轻一代,晋升甚至许多老一代工作根本不是同时进行的。 CMS在大多数情况下是并发的。 G1更像是一个停滞不前的增量收集器。 CMS和G1都具有重大且定期发生的世界停止事件,以及最坏的情况,通常使它们不适合严格的低延迟应用程序,例如金融交易或反应性用户界面。

可以使用其他收集器,例如Oracle JRockit Real Time,IBM Websphere Real Time和Azul Zing。 JRockit和Websphere收集器在大多数情况下都比CMS和G1具有延迟优势,但是经常遇到吞吐量限制,并且仍然遭受重大的世界末日事件。 Zing是该作者所知的唯一Java收集器,它可以真正地并发进行收集和压缩,同时保持所有代的高吞吐率。 Zing确实有一些毫秒级的世界停止事件,但这些事件是与收集周期中的相移有关的,这些相移与活动对象集的大小无关。

对于所包含的堆大小,高分配速率时,JRockit RT可以实现数十毫秒的典型暂停时间,但有时必须恢复到完全压缩暂停。 Websphere RT可以通过受限制的分配速率和活动集大小来实现单位毫秒的暂停时间。 通过在所有阶段(包括次要收集期间)并发执行,Zing可以以高分配率实现亚毫秒级的暂停。 无论堆大小如何,Zing都能够保持这种一致的行为,从而使用户可以根据需要应用大堆大小,从而满足应用程序吞吐量或对象模型状态需求,而不必担心增加暂停时间。

对于所有针对延迟的并发收集器,您必须放弃一些吞吐量并增加占用空间。 根据并发收集器的效率,您可能会放弃一点吞吐量,但始终会增加大量占用空间。 如果是真正的并发,几乎没有停滞事件,则需要更多的CPU内核来启用并发操作并保持吞吐量。

注意:分配足够的空间后,所有并发收集器往往会更有效地发挥作用。 作为一个经验法则,您应该将堆的预算至少为活动集大小的2到3倍,以进行有效的操作。 但是,用于维持并发操作的空间需求随着应用程序吞吐量以及相关的分配和提升速率而增长。 因此,对于更高吞吐量的应用程序,可以保证更高的堆大小与活动集比率。 鉴于当今系统可用的巨大内存空间,在服务器端很少出现问题。

垃圾收集监控和调整

要了解您的应用程序和垃圾收集器的行为方式,请至少使用以下设置启动JVM:

-verbose:gc
-Xloggc:
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-XX:+PrintTenuringDistribution
-XX:+PrintGCApplicationConcurrentTime 
-XX:+PrintGCApplicationStoppedTime

然后将日志加载到Chewiebug之类的工具中进行分析。

要查看GC的动态性质,请启动JVisualVM并安装Visual GC插件。 这将使您能够如下所示查看适用于您的应用程序的GC。

为了了解您的应用程序的GC需求,您需要可以重复执行的代表性负载测试。 当您掌握每个收集器的工作方式时,然后以不同的配置运行负载测试作为实验,直到达到吞吐量和延迟目标。 从最终用户的角度衡量延迟很重要。 这可以通过在直方图中捕获每个测试请求的响应时间来实现,您可以在此处了解更多信息。 如果您的延迟峰值超出可接受范围,请尝试将其与GC日志关联,以确定是否是GC问题。 其他问题可能会导致延迟峰值。 要考虑的另一个有用工具是jHiccup ,它可用于跟踪JVM中以及整个系统中的暂停。 用jHiccup测量您的空闲系统几个小时,您通常会感到非常惊讶。

如果延迟高峰是由于GC引起的,则投资调整CMS或G1以查看您的延迟目标是否可以实现。 有时由于高分配和提升率以及低延迟要求,这可能无法实现。 GC调整可以成为一项高技能的练习,通常需要更改应用程序以减少对象分配率或对象寿命。 如果是这种情况,则可能需要在时间和在GC调整和应用程序更改上花费的资源之间进行商业平衡,例如,可能需要购买商业并发压缩JVM中的一种,例如JRockit Real Time或Azul Zing。

参考: Java垃圾收集摘自我们的JCG合作伙伴 Martin Thompson在Mechanical Sympathy博客上的内容。

翻译自: https://www.javacodegeeks.com/2013/07/java-garbage-collection-distilled.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/368253.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

设计模式(二)模板方法模式

1.模版方法模式简介 模版方法模式介绍 在软件开发中&#xff0c;有时会遇到类似的情况&#xff0c;某个方法的实现需要多个步骤&#xff0c;其中有些步骤是固定的&#xff0c;而有些步骤并不固定&#xff0c;存在可变性。为了提高代码的复用性和系统的灵活性&#xff0c;可以…

题解 P2598 【[ZJOI2009]狼和羊的故事】

P2598 [ZJOI2009]狼和羊的故事 题目描述 “狼爱上羊啊爱的疯狂&#xff0c;谁让他们真爱了一场&#xff1b;狼爱上羊啊并不荒唐&#xff0c;他们说有爱就有方向&#xff0e;&#xff0e;&#xff0e;&#xff0e;&#xff0e;&#xff0e;” Orez听到这首歌&#xff0c;心想&am…

前端机试面试题

一、题目要求 1、请实现“https://channel.jd.com/fashion.html”超值购部分内容。 2、使用CSS DIV实现页面布局&#xff0c;页面居中&#xff0c;文字颜色效果要求一致。40分 3、鼠标悬停时的动画效果。10分 4、“进入查看”标签与样式。10分 5、定义一个javascript数组&…

三分大法好

三分算法解决凸形或者凹形函数的极值&#xff1b; 如下图 lmid (Left Right) / 2 rmid (lmid Right) / 2; 如果lmid靠近极值点&#xff0c;则Right rmid&#xff1b; 否则(即midmid靠近极值点)&#xff0c;则Left lmid; 例题的话在我的博客相关分类中找. 转载于:https://w…

将内存消耗减少20倍

这将是另一个故事&#xff0c;与我们分享有关内存相关问题的最新经验。 该案例是从最近的客户支持案例中提取的&#xff0c;在该案例中&#xff0c;我们遇到了一个行为异常严重的应用程序&#xff0c;该应用程序因生产中的OutOfMemoryError消息而死亡。 在连接了Plumbr的情况下…

Flex 布局教程:实例篇

该教程整理自 阮一峰Flexible教程 今天介绍常见布局的Flex写法。你会看到&#xff0c;不管是什么布局&#xff0c;Flex往往都可以几行命令搞定。 我的主要参考资料是Landon Schropp的文章和Solved by Flexbox。 一、骰子的布局 骰子的一面&#xff0c;最多可以放置9个点。 下面…

Apache Server和JMeter调试

我一直在使用JMeter为生产服务器生成负载以测试我的应用程序。 该测试计划具有13个以上的HTTP采样器以发出不同的请求&#xff0c;并具有一个正则表达式提取器以从响应中提取一些值。 此值在连续的HTTP Sampler中使用。 这个测试用例简单而直接。 最初&#xff0c;我使用200个J…

Flexible 弹性盒子模型之flex

实例 让所有弹性盒模型对象的子元素都有相同的长度&#xff0c;忽略它们内部的内容&#xff1a; #main div{flex:1;} 复制 效果预览 浏览器支持 表格中的数字表示支持该属性的第一个浏览器的版本号。 紧跟在 -webkit-, -ms- 或 -moz- 后的数字为支持该前缀属性的第一个版本。 …

java冒泡遍历对象_Java经典排序算法(冒泡、选择、插入)

排序算法说明排序说明对一序列对象根据某个关键字进行排序。术语说明稳定&#xff1a;如果a原本在b前面&#xff0c;而ab&#xff0c;排序之后a仍然在b的前面&#xff1b;不稳定&#xff1a;如果a原本在b的前面&#xff0c;而ab&#xff0c;排序之后a可能会出现在b的后面&#…

快速分类–三向和双枢轴

毫无疑问&#xff0c;Quicksort被认为是本世纪最重要的算法之一&#xff0c;并且它是许多语言&#xff08;包括Java中的Arrays.sort &#xff09;的事实上的系统排序。 那么&#xff0c;quicksort有何新功能&#xff1f; 嗯&#xff0c;除了我现在&#xff08;在Java 7发行了2…

在jekyll模板博客中添加网易云模块

最近使用GitHub Pages Jekyll 搭建了个人博客&#xff0c;作为一名重度音乐患者&#xff0c;博客里面可以不配图&#xff0c;但是不能不配音乐啊。 遂在博客里面引入了网易云模块&#xff0c;这里要感谢网易云的分享机制&#xff0c;对开发者非常友好&#xff1a; 1.每首歌曲…

线性规划对偶原理

留坑啊留坑。。 白天老师讲的都没听说过 晚上肯定整理不玩啊&#xff0c;&#xff0c;&#xff0c; 转载于:https://www.cnblogs.com/zwfymqz/p/8253842.html

java 18.9_Oracle: Java 11 (18.9 LTS) 正式上线!

美国时间9月25日&#xff0c;Oracle 官方表示&#xff0c; Java 11 (18.9 LTS) 正式上线!这是自 Java 8 后的第一个长期支持版本。Java 11 版本说明按照 Oracle 公布的支持路线图&#xff0c;Java 11 将会获得 Oracle 提供的长期支持服务&#xff0c;直至2026年9月。为了更快地…

CSS3新特性应用之用户体验

目录 背景与边框第一部分背景与边框第二部分形状视觉效果字体排印用户体验结构与布局过渡与动画源码下载 一、光标 新增加not-allowed光标&#xff0c;不允许访问隐藏光标&#xff0c;在触模应用上很有用&#xff0c;css2.1需要一个透明的图片来实现&#xff0c;而css3直接用…

day 60 Bootstrip学习

图标地址 http://fontawesome.io/icons/ 图标用法地址 http://fontawesome.io/examples/ 实现代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"I…

谈谈常用清除浮动的方法

我们在做页面布局的时候&#xff0c;经常需要利用浮动来实现一些布局效果&#xff0c;这样带来的后果就会导致父元素丢失宽度。今天我们就来说说‘找回‘宽度的方法。 而清除浮动后的效果应该是这样的请看&#xff1a; 下面就说说方法&#xff0c;方法其实有非常的多&#xf…

ArcGIS API For JS之网络分析(临近设施分析)

ArcGIS 提供两种网络分析&#xff0c;即基于Geometric Network的有向网络或者设施网络和基于Network Dataset的无向网络&#xff0c;在这里网络的分析指后者&#xff0c;ArcGIS api支持网络分析中的最短路径分析、服务区分析、临近设施分析。本文主要讲的是临近设施分析&#x…

UWP DEP0700: 应用程序注册失败。[0x80073CF9] 另一个用户已安装此应用的未打包版本。当前用户无法将该版本替换为打包版本。...

最近电脑抽风&#xff0c;我在【应用程序和功能】中重置了以下我的App自然灾害&#xff0c;居然&#xff0c;搞出大新闻了。 它居然从列表中消失了。。。 vs再次编译代码的时候&#xff0c;提示 严重性 代码 说明 项目 文件 行 禁止显示状态 错误 DEP…

泽西岛的RESTful Web服务

我已经讨论了有关体系结构注意事项<< link >>的早期文章&#xff0c;以成为可在我的系统/机器上使用的分布式环境上的RESTful系统。 本文我们将讨论如何基于REST体系结构考虑来构建Web服务。 本教程说明了如何使用Tomcat 6&#xff0c;Eclipse和Jersey JAX-RS&…

css水平垂直居中(绝对定位居中)

使用绝对定位有个限制就是父集必须设置一个固定的高度。 首先HTML 1 <div id"box"> 2 <div class"child"></div> 3 </div> CSS 1 #box {2 position: relative;3 height: 500px;4 background: red;5 }6 .chil…