知识蒸馏 循环蒸馏_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服务器应用程序的-XX:+ UseNUMA分配并行的收集器,有时可以为CPU套接字本地的线程分配Eden内存,从而获得性能优势。 遗憾的是,该功能对其他收集器不可用。

并发标记扫描(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会显着降低速度。 当将对象从一个区域撤离到另一个区域时,相关的世界停止事件的时间长度往往与需要扫描并可能需要打补丁的参考区域的数量成正比。

维护“已记住的集合”会增加次要集合的成本,从而导致停顿的时间要长于Parallel Old或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都有重大且定期发生的世界停止事件,以及最坏的情况,通常使它们不适用于严格的低延迟应用程序,例如金融交易或React性用户界面。

可以使用其他收集器,例如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/347701.shtml

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

相关文章

Javascript中的AES加密和Java中的解密

AES代表高级加密系统&#xff0c;它是一种对称加密算法&#xff0c;很多时候我们需要在客户端加密一些纯文本&#xff08;例如密码&#xff09;并将其发送到服务器&#xff0c;然后由服务器解密以进行进一步处理.AES加密和解密更加容易在相同的平台&#xff08;例如Android客户…

Java面向对象(14)--包装类的使用

针对八种基本数据类型定义相应的引用类型—包装类&#xff08;封装类&#xff09;&#xff0c;有了类的特点&#xff0c;就可以调用类中的方法。 基本数据类型 <——> 包装类&#xff0c;String——>包装类 ①装 int num 9; Integer int1 new Integer(num); Syste…

资源泄漏如何处理_处理缓慢的资源泄漏

资源泄漏如何处理使用Java监视器查找资源泄漏 查找缓慢的资源泄漏是使应用程序服务器长时间保持正常运行的关键。 在这里&#xff0c;我解释了如何使用Java监视器来发现缓慢的资源泄漏&#xff0c;以及如何验证它们是否是实际泄漏&#xff0c;而不仅仅是额外的预分配到某些HTTP…

mac php 超时,PHP---Mac上开启php错误提示

发现在使用mac 上 PHP开发项目的时候&#xff0c;程序代码错误的时候没有错误提示&#xff0c;只是提示白板。研究和查找资料才调整了一下; 步骤如下&#xff1a;1.找到php.ini文件如图所示&#xff1a;1)找到 display_errors Off &#xff0c;把Off 改为 On . 最后为 display…

tomcat 轮询_用Spring长轮询Tomcat

tomcat 轮询就像喜剧演员弗兰基 豪威尔 &#xff08; Frankie Howerd&#xff09;所说的“噢&#xff0c;小姐小姐” &#xff0c;但足够多的英国影射和双重诱惑&#xff0c;因为长轮询Tomcat对隔壁的闷气不是某种性的偏见&#xff0c;这是一种技术&#xff08;或更像是一种hac…

免费网络研讨会:Java应用程序中的吞咽异常

1月30日参加我们的网络研讨会&#xff0c;以发现Java应用程序中的“隐藏”异常。 如果一棵树落在森林中&#xff0c;但是没有写到原木上&#xff0c;它会发出声音吗&#xff1f; 答案是肯定的。 这些类型的错误可能会对用户体验造成严重影响&#xff0c;而没有根本原因的可见性…

Java面向对象(17)--类代码块

静态代码块&#xff1a;用static 修饰的代码块 ①可以有输出语句。 ②可以对类的属性、类的声明进行初始化操作。 ③不可以对非静态的属性初始化&#xff0c;即&#xff1a;不可以调用非静态的属性和方法。 ④ 静态代码块随着类的加载而加载并执行&#xff0c;类加载一次&…

js 和java有关系吗,javascript和JAVA有什么关系

2017-07-28Java调用javascriptpackage co。test;import java。io。FileReader;import java。io。LineNumberReader;import org。mozilla。 javascript。Context;import org。mozilla。javascript。Function;import org。mozilla。javascript。Scriptable;public class JSExplor…

php检测一个变量是否设置函数,php如何判断变量是否有设置的函数

php判断变量是否有设置的函数的方法&#xff1a;可以利用isset()函数来进行判断。isset()函数用于检测变量是否已设置并且非NULL。如果指定变量存在且不为NULL&#xff0c;则返回TRUE&#xff0c;否则返回FALSE。isset() 函数用于检测变量是否已设置并且非 NULL。(推荐教程&…

mockito_吸收Mockito的流利度

mockito我最近发现自己编写了一些代码来集成两个不同的平台。 这些系统之一是基于Java的系统&#xff0c;而另一个虽然不是用Java编写的&#xff0c;却提供了Java API。 我将这些系统分别称为Foo和Bar。 在我编写一行代码之前就很明显了&#xff0c;但是&#xff0c;测试最终…

Java异常处理(1)--异常概述与异常体系结构

在Java语言中&#xff0c;将程序执行中发生的不正常情况称为“异常”。(开发过程中的语法错误和逻辑错误不是异常) Java程序在执行过程中所发生的异常事件可分为两类&#xff1a; ①Error&#xff1a;Java虚拟机无法解决的严重问题。如&#xff1a;JVM系统内部错误、资源耗尽等…

Payara Micro在Oracle应用容器云上

在此博客文章中&#xff0c;我将介绍如何将打包在Payara Microber -jar中的CloudEE Duke应用程序部署到Oracle Application Container Cloud 。 在Oracle Application Container Cloud中进行部署所需的部署工件是一个ZIP归档文件&#xff0c;其中包含应用程序ber-jar和清单文件…

Java异常处理(2)--异常处理机制及自定义异常

在编写程序时&#xff0c;经常要在可能出现错误的地方加上检测的代码&#xff0c;如进行x/y运算时&#xff0c;要检测分母为0&#xff0c;数据为空&#xff0c;输入的不是数据而是字符等。过多的if-else分支会导致程序的代码加长、臃肿&#xff0c;可读性差。因此采用异常处理机…

jaas_受JAAS保护的JAX-RS端点

jaas随着RESTFUL&#xff08;JAX-RS&#xff09;作为创建Web服务端点的“首选”方式的问世&#xff0c;很长一段时间以来&#xff0c;我一直想知道人们如何围绕它实现安全机制。 归根结底&#xff0c;我假设JAX-RS的基础实现是servlet&#xff0c;因此其安全性也可能围绕容器&…

Java多线程(1)--基本概念:程序、进程、线程

程序(program)是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码&#xff0c;静态对象。 进程(process)是程序的一次执行过程&#xff0c;或是正在运行的一个程序&#xff0c;是一个动态的过程&#xff1a;有它自身的产生、存在和消亡的过程。——生命周…

Java中的Volatile如何工作? Java中的volatile关键字示例

如何在Java中使用Volatile关键字 在Java采访中&#xff0c;什么是volatile变量以及何时在Java中使用volatile变量是Java 采访中一个著名的多线程采访问题 。 尽管许多程序员都知道什么是volatile变量&#xff0c;但是他们在第二部分上失败了&#xff0c;即在Java中何处使用vol…

Java线程的调度及线程的优先级

调度策略 Java的调度方法 同优先级线程组成先进先出队列&#xff08;先到先服务&#xff09;&#xff0c;使用时间片策略。 对高优先级&#xff0c;使用优先调度的抢占式策略。 线程的优先级等级 Thread.MAX_PRIORITY&#xff1a;10 Thread.MIN _PRIORITY&#xff1a;1 Threa…

Spring Boot 2应用程序和OAuth 2 –传统方法

这篇文章是3个系列文章中的第二部分&#xff0c;探讨了如何为基于Spring Boot 2的应用程序启用OSO2提供程序SSO。 3个帖子是&#xff1a; 1. 引导兼容OpenID Connect的OAuth2授权服务器/ OpenID提供程序的方法 2.与OAuth2授权服务器/ OpenID提供程序集成的旧版Spring Boot / …

Java多线程(2)--Thread类继承和Runnable接口创建线程

Java语言的JVM允许程序运行多个线程&#xff0c;它通过java.lang.Thread类来体现。 Thread类的特性 每个线程都是通过某个特定Thread对象的run()方法来完成操作的&#xff0c;经常把run()方法的主体称为线程体&#xff0c;通过该Thread对象的start()方法来启动这个线程&#x…

matlab在电磁场与电磁波中的应用,matlab在电磁场与电磁波学习中的应用.docx

matlab在电磁场与电磁波学习中的应用.docx MATLAB在电磁场与电磁波学习中的应用裴逸菲(燕京理工学院信息科学与技术学院&#xff0c;河北廊坊065201)摘要针对电磁场与电磁波在大学课程中的理论性强、概念抽象的特点&#xff0c;在学习中引入MATLAB软件&#xff0c;利用MATLAB的…