Java

目录

1 简介

此 Java 调优白皮书旨在作为 Java 性能调优信息、技术和指针的参考。

1.1 目标

本白皮书的目标是在一个地方收集 Java 性能的最佳实践和“操作方法”。

本调整文档的初始目标是调整大型多处理器服务器上的服务器应用程序。本文档的未来版本将探讨有关桌面 Java 性能的类似建议。

1.2 这是一个活文件

本白皮书的编写充分考虑了 Sun 的 Java™ HotSpot™ 虚拟机的最新进展。因此,本文档将频繁更新,以反映最新的性能特性和新的最佳实践。

1.3 如何使用本白皮书

本白皮书分为几个部分,从获得更好 Java 性能的最简单、最容易获得的方法到逐渐复杂的调优和设计建议。

最佳实践开始, 以确保您在进行任何调优之前获得尽可能最佳的 Java 性能。在深入研究性能调优之前,必须了解从数据做出决策的正确方法 。只有在定向分析的基础上,您才能安全地继续为您的应用程序探索 调优思路。为什么会有“调整思路”而不仅仅是一揽子建议?因为每个应用程序都是不同的,没有一套建议适合每个部署环境。调优思路部分不仅旨在为您提供 Java 命令行选项,还旨在为您提供有关它们的含义以及何时可以提高性能的背景知识。

即使在调优过程中,但当然,当您希望将性能提升到一个新的水平时,就有必要探索 监控和分析您的应用程序。通过收集有关实际应用程序性能的详细数据,您可以微调命令行选项并了解将编码改进工作重点放在何处。在关于性能编码的部分中, 我们将介绍在考虑性能的情况下构建应用程序时要考虑的 Java™ API。

许多其他文档和站点将被收集在 指针部分。我们鼓励您通过反馈和 Java 性能社区帮助继续使 Java 更快 。您的反馈将有助于完善本白皮书。

2 最佳实践

有许多最佳实践可以让您的 Java 应用程序获得最佳性能。在开始调优之前,这里有一些非常基本的实践,它们可以显着提高性能。

2.1 使用最新的 Java™ 版本

Java™ 的每个主要版本都引入了新功能、错误修复和性能增强。在开始调整或考虑更改应用程序级别以利用新的语言功能之前,升级到最新的主要 Java™ 版本仍然是值得的。有关为什么升级到 Java SE 5.0 (Tiger) 很重要的信息,请参阅指针部分中的参考资料 。

可以理解,这并不总是可行,因为某些应用程序,尤其是第三方 ISV 应用程序可能尚未提供对最新版本的 Java™ 的支持。在这种情况下,请使用您的应用程序支持的最新 更新版本(并请鼓励您的 ISV 支持最新的 Java™ 版本!)。

2.2 获取最新的 Java™ 更新版本

对于每个主要的 Java™ 版本“系列”(例如 J2SE 1.3.1、J2SE 1.4.2、J2SE 5.0),Sun 都会定期发布更新版本。例如,J2SE 5.0 的最新更新版本是 更新 6Java SE 1.5.0_06

更新版本通常包括错误修复和性能改进。通过部署 Java™ 的最新更新版本,您将受益于最新和最大的性能改进。

2.3 确保您的操作系统补丁是最新的

尽管 Java 是跨平台的,但它确实依赖于底层操作系统,因此 Java™ 平台的操作系统基础尽可能保持最新非常重要。

对于 Solaris,在部署 Java 应用程序时推荐使用一组补丁。要获取这些适用于 Java 的 Solaris 修补程序,请参阅Java™ 下载页面上Solaris OS 修补程序部分下的链接 。

2.4 消除可变性

请注意,在您的系统上运行的各种系统活动和其他应用程序的操作可能会给任何应用程序性能的测量带来显着差异,包括 Java 应用程序。操作系统和其他应用程序的活动可能会导致 CPU、内存、磁盘或网络资源争用,这些争用可能会干扰您的测量。

在您开始衡量Java性能试图评估应用程序的行为可能会 变色的性能结果(如接入互联网进行更新,从用户的主目录读取文件等)。通过尽可能简化应用程序行为并一次仅更改一个变量(即操作系统可调参数、Java 命令行选项、应用程序参数等),您的性能调查可以独立跟踪每个更改的影响。

3 从数据中做出决策

在更改之前和之后运行一次应用程序并得出关于该更改的影响的结论确实很诱人。有时应用程序会运行很长时间。有时启动应用程序可能很复杂并且依赖于多个外部服务。但是你能从这个测试中合理地做出决定吗?除非您定量地衡量数据的威力,否则真的不可能知道您是否可以安全地从数据中得出结论。在设计任何一组实验时,应用科学方法很重要。在测量 Java 应用程序性能时,严格性尤其必要,因为 Java™ HotSpot™ 虚拟机的行为会适应并响应它正在运行的特定机器和特定应用程序。

3.1 小心微基准测试!

Java 的优势之一是它在运行时动态优化数据。我们越来越多地发现 Java 性能达到或超过类似静态编译程序的性能的情况。然而,JVM 的这种适应性使得很难衡量 Java 功能的小片段。

衡量 Java 性能具有挑战性的原因之一是它会随着时间而变化。在启动时,JVM 通常会花一些时间“预热”。根据 JVM 实现,它可能会在解释模式下花费一些时间,同时分析它以找到“热”方法。当一个方法变得足够热时,它可能会被编译并优化为本机代码。

事实上,其中一些优化可能会展开循环、在循环外提升变量甚至完全消除“死代码”。那么你应该如何处理呢?您是否应该通过计时几次循环迭代来考虑机器速度的差异,然后运行多次迭代,以便在每个平台上获得相似的总运行时间?如果您这样做,可能会发生的是您的计时循环将在解释模式下运行时估计循环时间。然后在运行测试时,循环将得到优化并运行得更快。事实上,如此之快,以至于您的总运行时间可能如此之短,以至于您根本没有测量内部循环,而只是测量用于预热应用程序的基础设施。

对于某些应用程序,垃圾收集会使编写基准变得复杂。然而,重要的是要注意,对于给定的一组调整参数,GC 吞吐量是可预测的。因此,要么避免在内部循环中分配对象(以避免引发 GC),要么运行足够长的时间以达到 GC 稳定状态。如果您确实将对象分配为基准测试的一部分,请小心调整堆的大小,以最大限度地减少 GC 的影响并收集足够的样本,以便您获得在 GC 上花费的时间的公平平均值。

基准测试还有更微妙的陷阱。如果循环内部的工作对于每次迭代并不是真的恒定怎么办?例如,如果您追加到一个字符串,您可能会在追加之前进行复制,这将增加每次执行循环时的工作量。请记住尝试使循环中的计算恒定且非平凡。通过打印最终答案,您将防止整个循环体被完全消除。

显然,运行到稳定状态对于获得可重复的结果至关重要。考虑运行基准测试几分钟。任何运行时间少于一分钟的应用程序都可能受 JVM 启动时间的支配。另一个可能发生的时序问题是抖动。当计时机制的粒度比基准计时的可变性粗得多时,就会发生抖动。例如,在某些 Windows 平台上,调用 的有效粒度为 15 毫秒。对于短期测试,这往往会使结果变色。任何新的基准测试都应该利用可以减少这种抖动的新 方法。 System.currentTimeMillis() System.nanoTime()

3.2 使用统计

在实验之前,您应该尝试消除尽可能多的应用程序性能变化。然而,几乎不可能消除 所有可变性,尤其是来自异步操作系统服务的噪音。通过在多次试验过程中重复相同的实验并平均结果,您可以有效地关注信号而不是噪声。改善信号的速率与样本数的平方根成正比。

为了将 Java 基准测试纳入统计术语,我们将测试更改前的 基线设置和更改后的 样本设置。例如,您可以在没有 Java 命令行选项的情况下运行 10 次基准基准测试,并使用“-server”命令行运行 10 次样本测试。这将为您提供两个不同的样本总体。您真正想回答的问题是,“Java 设置中的更改是否有所不同?如果有,差异有多大?”。

第二个问题,确定百分比改进实际上是更容易的问题:

percentageImprovement = 100.0 x (SpecimenAvg - BaselineAvg) / BaselineAvg

第一个问题“Java 设置中的更改是否有所不同?”是最重要的问题,但是因为我们真正想知道的是“差异是否足够显着以至于我们可以安全地从中得出结论?”。用统计学术语来说,这个问题可以改写为“这两个样本群体是否反映了相同的潜在群体?”。为了回答这个问题,我们使用学生 t 检验。有关学生 t 检验的更多背景信息,请参阅 指针部分。使用基线群体和样本群体的样本数量、平均值和标准偏差以及设置所需的风险水平 ( alpha ) 我们可以确定 p 值或 Java 设置更改显着的可能性。风险水平 ( alpha ) 通常设置为 0.05(或 0.01),这意味着,即使没有,您也会在 100 次中有 5 次(一次)发现平均值之间存在统计上的显着差异。一般来说,如果 p 值小于 0.05,那么我们会说差异是显着的,因此我们可以得出结论,Java 设置的更改确实有所不同。有关解释 p 值和功效分析的更多信息,请参阅 假设检验的逻辑

3.3 使用基准线束

什么是 基准线束?基准测试工具通常是一个脚本程序,用于启动给定的基准测试、捕获输出日志文件并提取基准测试分数(和子分数)。通常,基准测试工具可以在预定次数的试验中运行基准测试,并且理想情况下,根据不同 Java 设置的结果计算统计数据(无论设置更改是在操作系统级别、Java 调整还是编码级别差异)。

一些基准测试包含一个线束。即使在这些情况下,您也希望重新编写工具以测试更广泛的参数、捕获附加数据和/或计算附加统计信息。编写一个简单的基准测试工具的优点是它可以消除收集许多样本的乏味,它可以以一致的方式启动应用程序,并且可以简化计算统计数据的过程。

无论您是否使用基准测试工具,都必须确保在尝试 Java 调整更改时能够回答以下问题:“我是否收集了足够多的样本以提供足够的统计显着性,以便我可以从结果中得出结论? ?”。

4 调优思路

到目前为止,您已经采取了最佳实践部分中的简单步骤, 并通过了解从数据做出决策的正确方法来准备进行调优 。关于调优思路的这一部分包含有关您应该在 Java 应用程序中尝试的各种调优选项的建议。在不同选项集之间进行的所有比较都应使用上述统计技术进行。

4.1 一般调优指南

以下是一些通用调优指南,可帮助您对将执行的 Java 调优类型进行分类。

4.1.1 注意人体工程学设置

在开始调整 Java 的命令行参数之前,请注意 Sun 的 HotSpot™ Java 虚拟机已经整合了开始调整自身的技术。这种智能调整被称为 人体工程学。大多数具有至少 2 个 CPU 和至少 2 GB 物理内存的计算机被视为 服务器级计算机,这意味着默认设置为:

  • -server编译器
  • -XX:+UseParallelGC并行(吞吐量)的垃圾收集器
  • -Xms初始堆大小是本机的物理存储器的1/64
  • -Xmx最大堆大小是本机的物理存储器的1/4(高达1 GB最大值)。

请注意,32 位 Windows 系统-client默认都使用 编译器,符合上述条件的 64 位 Windows 系统将被视为服务器级机器。

4.1.2 堆大小

尽管人体工程学显着改善了许多应用程序的“开箱即用”体验,但优化调整通常需要更多地关注 Java 内存区域的大小。

Java 应用程序的最大堆大小受三个因素的限制:进程数据模型(32 位或 64 位)和相关的操作系统限制、系统上可用的虚拟内存量以及物理内存量系统上可用。特定应用程序的 Java 堆大小永远不会超过甚至达到进程数据模型的最大虚拟地址空间。对于 32 位进程模型,进程的最大虚拟地址大小通常为 4 GB,但某些操作系统将其限制为 2 GB 或 3 GB。最大堆大小通常为 -Xmx3800m (1600m)(对于 2 GB 限制),但实际限制取决于应用程序。对于 64 位进程模型,最大值基本上是无限的。对于专用系统上的单个 Java 应用程序,Java 堆的大小不应设置为系统上的物理 RAM 量,因为操作系统、其他系统进程甚至其他 JVM 操作都需要额外的 RAM。提交过多的系统物理内存可能会导致虚拟内存分页到磁盘,很可能在垃圾收集操作期间,导致严重的性能问题。在具有多个 Java 进程或通常有多个进程的系统上,这些进程的 Java 堆总和也不应超过系统中物理 RAM 的大小。s 物理内存很可能导致虚拟内存分页到磁盘,很可能在垃圾收集操作期间,导致严重的性能问题。在具有多个 Java 进程或通常有多个进程的系统上,这些进程的 Java 堆总和也不应超过系统中物理 RAM 的大小。s 物理内存很可能导致虚拟内存分页到磁盘,很可能在垃圾收集操作期间,导致严重的性能问题。在具有多个 Java 进程或通常有多个进程的系统上,这些进程的 Java 堆总和也不应超过系统中物理 RAM 的大小。

下一个最重要的 Java 内存可调参数是年轻代的大小(也称为 NewSize)。一般来说,年轻代的最大推荐值是最大堆大小的3/8。请注意,使用吞吐量和低暂停时间收集器可能会超过此比率。有关更多信息,请参阅使用 5.0 Java 虚拟机文档调整垃圾收集中对年轻代保证的讨论 。

下面将更详细地介绍其他内存设置,例如堆栈大小。

4.1.3 垃圾收集器策略

Java™ 平台提供了垃圾收集算法的选择。对于这些算法中的每一种,都有各种策略可调参数。这里不再重复Tuning Garbage Collection文档的细节,而是 说前两种选择对于大型服务器应用程序来说是最常见的:

  • -XX:+UseParallelGC并行(吞吐量)的垃圾收集器,或
  • -XX:+UseConcMarkSweepGC并发(低暂停时间)垃圾收集器(也被称为CMS)
  • 所述 -XX:+UseSerialGC串行垃圾收集器(用于较小的应用程序和系统)

4.1.4 其他调优参数

此处将提及某些其他对性能有很大影响的 Java 调优参数。有关 Java 调优参数的全面参考,请参阅 指针部分。

VM选项页讨论大内存分页Java支持。通过适当地配置操作系统,然后使用命令行选项 -XX:+UseLargePages对于 Solaris,默认情况下打开), -XX:LargePageSizeInBytes您可以从服务器的内存管理系统中获得最佳效率。请注意,使用更大的页面大小,我们可以更好地利用虚拟内存硬件资源 (TLB),但这可能会导致永久代和代码缓存的空间更大,这反过来又会迫使您减小 Java 堆的大小. 这是 2 MB 或 4 MB 页面大小的一个小问题,但是 256 MB 页面大小的一个更有趣的问题。

特定于 Solaris 的可调参数的一个示例是选择 libumem替代堆分配器。要在 Solaris 上试验 libumem,您可以使用以下 LD_PRELOAD 环境变量指令:

  • 要为给定 shell 的所有子进程设置 libumem,请设置并导出环境变量

    LD_PRELOAD=/usr/lib/libumem.so

  • sh使用libumem启动 Java 应用程序 :

    LD_PRELOAD=/usr/lib/libumem.so java java-settings application-args

  • csh使用libumem启动 Java 应用程序 :

    env LD_PRELOAD=/usr/lib/libumem.so java java-settings application-args

您可以通过使用pmap(1)pldd(1)验证进程设置来验证 libumem是否正在使用中 。

4.2 调优示例

以下是用于您的实验的一些特定调整示例。请理解,这些只是示例,您的硬件上的应用程序的最佳堆大小和调整参数可能会有所不同。

4.2.1 调优示例 1:吞吐量调优

以下是在具有 4 GB 内存并能够同时运行 32 个线程(CPU 和内核或上下文)的系统上运行的服务器应用程序的特定命令行调整示例。

java -Xmx3800m -Xms3800m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20

注释:

  • -Xmx3800m -Xms3800m

    配置大型 Java 堆以利用大型内存系统。

  • -Xmn2g

    为年轻代配置一个大堆(可以并行收集),再次利用大内存系统。它有助于防止生命周期较短的对象过早提升到老年代,因为老年代的垃圾收集成本更高。

  • -Xss128k

    减少默认的最大线程堆栈大小,这允许 Java 堆使用更多进程的虚拟内存地址空间。

  • -XX:+UseParallelGC

    为新一代 Java 堆选择并行垃圾收集器(注意:这通常是服务器级机器上的默认设置 )

  • -XX:ParallelGCThreads=20

    减少垃圾收集线程的数量。默认值将等于处理器数量,这在具有 32 线程能力的系统上可能会不必要地高。

4.2.2 调优示例 2:尝试并行老年代收集器

与示例 1 类似,我们在这里要测试并行老年代收集器的影响。

java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC

注释:

  • -Xmx3550m -Xms3550m

    尺寸已减小。ParallelOldGC 收集器具有额外的本机非 Java 堆内存要求,因此在运行 32 位 JVM 时可能需要减小 Java 堆大小。

  • -XX:+UseParallelOldGC

    使用并行的老年代收集器。老年代收集的某些阶段可以并行执行,从而加快老年代收集的速度。

4.2.3 调优示例 3:尝试 256 MB 页面

此调整示例特定于那些支持 256 MB 大页面大小的基于 Solaris 的系统。

java -Xmx2506m -Xms2506m -Xmn1536m -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC -XX:LargePageSizeInBytes=256m

注释:

  • -Xmx2506m -Xms2506m

    大小已减少,因为使用大页面设置会导致永久代和代码缓存大小为 256 MB,这会减少 Java 堆可用的内存。

  • -Xmn1536m

    年轻代堆的大小通常是整个 Java 堆大小的一小部分。通常,我们建议您从占整个堆大小 1/4 的年轻代大小开始调整。在这种情况下,年轻代被减少,以保持之前使用的示例选项中使用的年轻代和年老代大小之间的相似比率。

  • -XX:LargePageSizeInBytes=256m

    使 Java 堆(包括永久代)和已编译的代码缓存使用最小大小为 256 MB 的页面(对于支持它的平台)。

4.2.4 调优示例 4:尝试 -XX:+AggressiveOpts

此调整示例类似于示例 2,但添加了 AggressiveOpts 选项。

java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC -XX:+AggressiveOpts

注释:

  • -Xmx3550m -Xms3550m

    由于我们不再使用大页面,大小已增加回示例 2 的水平。

  • -Xmn2g

    由于我们不再使用大页面,大小已增加回示例 2 的水平。

  • -XX:+AggressiveOpts

    打开预期在即将发布的版本中默认启用的点性能优化。按此标志分组的更改是对 JVM 运行时编译代码的微小更改,而不是明显的性能特性(例如 BiasedLocking 和 ParallelOldGC)。这是一个很好的标志,可以尝试 JVM 工程团队为即将发布的版本进行的最新性能调整。注意:这个选项是 实验性的!此选项启用的特定优化可能会因版本而异,甚至会因构建而异。在部署新版本的 Java 之前,您应该重新评估此选项的效果。

4.2.5 调优示例 5:尝试偏置锁定

此调整示例建立在示例 4 的基础上,并添加了偏置锁定选项。

java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC -XX:+AggressiveOpts -XX:+UseBiasedLocking

注释:

  • -XX:+UseBiasedLocking

    启用一种用于提高无竞争同步性能的技术。一个对象被“偏置”朝向经由其首先获取其监视器的线程 monitorenter字节码或同步方法调用; 该线程执行的后续监视器相关操作在多处理器机器上相对要快得多。启用此标志后,一些具有大量无竞争同步的应用程序可能会获得显着的加速;某些具有某些锁定模式的应用程序可能会出现减速,但已尝试将负面影响降至最低。

4.2.6 调优示例 6:针对低暂停时间和高吞吐量进行调优

此调优示例类似于示例 2,但使用并发垃圾收集器(而不是并行吞吐量收集器)。

java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:ParallelGCThreads=20 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:SurvivorRatio=8 -XX:TargetSurvivorRatio=90 -XX:MaxTenuringThreshold=31

注释:

  • -XX:+UseConcMarkSweepGC -XX:+UseParNewGC

    选择并发标记扫描收集器。此收集器可为应用程序提供更好的响应时间属性(即,应用程序暂停时间较短)。它是一个并行且主要是并发的收集器,并且可以很好地匹配大型多处理器系统的线程能力。

  • -XX:SurvivorRatio=8

    将幸存者空间比例设置为 1:8,导致更大的幸存者空间(比例越小,空间越大)。更大的幸存者空间允许短命对象在年轻代中死亡更长的时间。

  • -XX:TargetSurvivorRatio=90

    允许 90% 的 Survivor 空间被占用,而不是默认的 50%,从而更好地利用 Survivor 空间内存。

  • -XX:MaxTenuringThreshold=31

    允许生命周期较短的对象在年轻代中死亡更长的时间(因此避免升级)。此设置的结果是,由于要复制的额外对象,次要 GC 时间可能会增加。可能需要调整此值和幸存者空间大小,以平衡幸存者空间之间复制的开销与将长期存在的长期对象。CMS 的默认设置是 SurvivorRatio=1024MaxTenuringThreshold=0,这会导致所有清理的幸存者都被提升。这会给收集年老代的单个并发线程带来很大压力。注意:与 一起使用时 -XX:+UseBiasedLocking,此设置应为 15。

4.2.7 调优示例 7:尝试 AggressiveOpts 以获得低暂停时间和高吞吐量

此调整示例建立在示例 6 的基础上,并添加了 AggressiveOpts 选项。

java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:ParallelGCThreads=20 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:SurvivorRatio=8 -XX:TargetSurvivorRatio=90 -XX:MaxTenuringThreshold=31 -XX:+AggressiveOpts

注释:

  • -XX:+AggressiveOpts

    打开预期在即将发布的版本中默认启用的点性能优化。按此标志分组的更改是对 JVM 运行时编译代码的微小更改,而不是明显的性能特性(例如 BiasedLocking 和 ParallelOldGC)。这是一个很好的标志,可以尝试 JVM 工程团队为即将发布的版本进行的最新性能调整。注意:这个选项是 实验性的!此选项启用的特定优化可能会因版本而异,甚至会因构建而异。在部署新版本的 Java 之前,您应该重新评估此选项的效果。

5 监控和分析

讨论监控(从正在运行的应用程序中提取高级统计数据)或分析(检测应用程序以提供详细的性能统计数据)本身就是值得白皮书的主题。出于本 Java 调优白皮书的目的,这些主题将使用工具作为示例进行介绍,这些工具可以永久免费使用。

5.1 监控

Java™ 平台带有大量内置的监控工具。有关更多信息,请参阅Java™ 平台的监控和管理文档 。

这些“内置”工具中最流行的是 JConsole 和 jvmstat技术。

5.2 剖析

Java™ 平台还包括一些分析工具。这些“内置”分析工具中最受欢迎的是 The -XprofProfiler HPROF profiler

基于JFluid 技术的分析器 已被整合到流行的 NetBeans开发工具中。

6 性能编码

本节将介绍您可以进行的编码级别更改,这些更改将对性能产生影响。为了 Java 调优白皮书的初稿,可能对性能产生影响的各种编码级别更改示例利用了新语言功能,如 NIO 和并发实用程序。

新的I / O API的(或NIO)提供像内存映射文件和可扩展的网络操作的操作更高的性能。通过使用 NIO,开发人员可能能够显着提高内存或网络密集型应用程序的性能。

另一个影响性能的新 Java 语言功能示例是 Concurrency Utilities 集。越来越多的服务器应用程序将面向具有多个 CPU 和每个 CPU 多个内核的平台。为了最好地利用这些系统,在设计应用程序时必须考虑到多线程。由于线程交互中的微妙之处,例如竞争条件,经典的多线程编程非常复杂且容易出错。现在有了 Concurrency Utilities,开发人员终于有了一组可靠的构建块,可以在这些构建块上构建可扩展的多线程应用程序,同时避免编写多线程框架的大部分复杂性。

7 指针

以下是本文档中引用的资源。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容