精华内容
下载资源
问答
  • 选择江西省吉水县天然林森林资源与生态环境的有关指标,通过变异系数法筛选得到天然林区域类型划分的基础指标体系。采用主成分分析方法对多维森林资源与环境指标进行降维,获得主成分分析的第一至第三主分量,利用...
  • 行业资料-电子功用-用于配电网可靠性分析的负荷点故障区域类型划分方法
  • 基于类型划分的我国区域水资源利用效率比较,陈刚,刘凤朝,本文在研究我国水资源分布和配置情况的基础上,结合各省经济水平、水资源禀赋以及水资源开发利用程度,对我国省级行政区进行分类
  • 我国煤田分布广、煤层多、储量大,矿床水文地质类型复杂,开采...在考虑成煤时代、充水水源类型、充水方式及充水途径等因素,对我国煤炭基地规划矿区进行水文地质类型划分,为煤炭资源科学开采和区域水资源保护提供了依据。
  • 根据长江流域各省(市、区)的经济发展水平、社会发展水平、资源环境状况和可持续发展能力,建立了区域可持续发展评价指标体系,对其可持续发展现状进行综合加权评价,采用聚类分析法划分不同类型.差异主要体现在经济发展...
  • 测试类型划分

    2020-03-15 16:50:56
    6. 按地域划分 7. 按测试对象划分 测试类型划分 1. 按研发阶段划分: 说明: 1.TDD: 表示测试驱动开发. 在编码前进行. 即就是 研发人员是拿着测试人员写的测试用例编写代码的 2.在V模型里, 单元测试...

    目录

     

    测试类型的划分

    1. 按研发阶段划分:

    2. 按实施组织划分:

    3. 按是否运行划分:

    4. 按是否手工划分:

    5. 按是否查看代码划分:

    6. 按地域划分

    7. 按测试对象划分


    测试类型的划分

    1. 按研发阶段划分:

    说明:

    1.TDD: 表示测试驱动开发. 在编码前进行. 即就是 研发人员是拿着测试人员写的测试用例编写代码的

    2.在V模型里, 单元测试对应的是详细设计. 因此单元测试的测试依据就是详细设计.

    3.集成测试采用灰盒测试, 即白盒+黑盒. 因为测试多模块之间的接口使用的是白盒测试, 测试集成之后的功能使用的是黑盒测试. 所以加起来, 就是灰盒测试.

    4.系统测试的测试对象是整个系统, 包括软件+硬件, 指的是"不仅测软件, 还要测软件部署在硬件(如电脑)上, 对硬件进行测试", 比如软件在硬件上不一定能够安装成功.

    2. 按实施组织划分:

    说明:

    1.α测试的人员, 要求是除本项目的研发人员和测试人员的公司内部人员, 其他项目的研发人员和测试人员可以

    2.β测试的测试人员是用户, β测试是验收测试的一种.

    3. 按是否运行划分:

    说明:

    1.按是否运行划分为静态和动态.

    2.静态和动态核心区别在于: 静态是不运行程序的, 动态是运行程序的.

    4. 按是否手工划分:

    5. 按是否查看代码划分:

    说明:

    1.黑盒测试就是功能测试, 比如说:通常在电脑上对软件进行一系列的操作, 操作完之后看出现的结果是否是我们想要的结果, 这个过程就是黑盒测试.

    2.白盒测试就是对代码进行测试, 测试代码的业务逻辑, 数据结构, 错误处理, 边界值, 路径覆盖等

    6. 按地域划分

    7. 按测试对象划分

    说明:

    1.按测试对象划分为: 业务,功能,性能,安全,界面,易用,一致性,可靠性,稳定性

    2.平时所练习的测试用例的分类, 就是按照测试对象来划分的.

    3.重点是这一种测试对象划分的类型是在后期进行的系统测试里用到的核心方法, 这些对象是系统测试的测试内容

     

     

    展开全文
  • 通道线、区域划分线、定位线的类型.doc
  • OSPF 有五种类型的协议报文: 1、Hello 报文:周期性发送,用来发现和维持 OSPF 邻居关系,以及进行 DR(Designated Router,指定路由器)/BDR(Backup Designated Router,备份指定路由器)的选举。 2、DD...

    一、OSPF报文

    OSPF 协议报文直接封装为 IP 报文,协议号为 89。

    OSPF 有五种类型的协议报文:

    1、Hello 报文:周期性发送,用来发现和维持 OSPF 邻居关系,以及进行 DR(Designated

    Router,指定路由器)/BDR(Backup Designated Router,备份指定路由器)的选举。

    2、DD(Database Description,数据库描述)报文:描述了本地 LSDB(Link State DataBase,

    链路状态数据库)中每一条 LSA(Link State Advertisement,链路状态通告)的摘要信息,

    用于两台路由器进行数据库同步。

    3、 LSR(Link State Request,链路状态请求)报文:向对方请求所需的 LSA。两台路由器互相

    交换 DD 报文之后,得知对端的路由器有哪些 LSA 是本地的 LSDB 所缺少的,这时需要发送

    LSR 报文向对方请求所需的 LSA。

    4、 LSU(Link State Update,链路状态更新)报文:向对方发送其所需要的 LSA。

    5、 LSAck(Link State Acknowledgment,链路状态确认)报文:用来对收到的 LSA 进行确认。

    二、LSA类型

    OSPF 中对链路状态信息的描述都是封装在 LSA 中发布出去,常用的 LSA 有以下几种类型:

    1、 Router LSA(Type-1):由每个路由器产生,描述路由器的链路状态和开销,在其始发的区域

    内传播。

    2、 Network LSA(Type-2):由 DR 产生,描述本网段所有路由器的链路状态,在其始发的区域

    内传播。

    3、 Network Summary LSA(Type-3):由 ABR(Area Border Router,区域边界路由器)产生,

    描述区域内某个网段的路由,并通告给其他区域。

    4、 ASBR Summary LSA(Type-4):由 ABR 产生,描述到 ASBR(Autonomous System

    Boundary Router,自治系统边界路由器)的路由,通告给相关区域。

    5、AS External LSA(Type-5):由 ASBR 产生,描述到 AS(Autonomous System,自治系统)

    外部的路由,通告到所有的区域(除了 Stub 区域和 NSSA 区域)。

    6、 NSSA External LSA(Type-7):由 NSSA(Not-So-Stubby Area)区域内的 ASBR 产生,描

    述到 AS 外部的路由,仅在 NSSA 区域内传播。

    7、 Opaque LSA:用于 OSPF 的扩展通用机制,目前有 Type-9、Type-10 和 Type-11 三种。其

    中,Type-9 LSA 仅在本地链路范围进行泛洪,用于支持 GR(Graceful Restart,平滑重启)

    的 Grace LSA 就是 Type-9 的一种类型;Type-10 LSA 仅在区域范围进行泛洪,用于支持

    MPLS TE 的 LSA 就是 Type-10 的一种类型;Type-11 LSA 可以在一个自治系统范围进行泛

    洪。

    三、OSPF区域

    OSPF基础,报文类型,区域划分,LSA类型,一分钟了解下

     

    1、区域的边界是路由器,而不是链路。一个路由器可以属于不同的区域,但是一个网段(链路)只能

    属于一个区域,或者说每个运行 OSPF 的接口必须指明属于哪一个区域。划分区域后,可以在区域

    边界路由器上进行路由聚合,以减少通告到其他区域的 LSA 数量,还可以将网络拓扑变化带来的影

    响最小化。

    2、骨干区域与虚连接

    1) 骨干区域(Backbone Area)

    OSPF 划分区域之后,并非所有的区域都是平等的关系。其中有一个区域是与众不同的,它的区域

    号是 0,通常被称为骨干区域。骨干区域负责区域之间的路由,非骨干区域之间的路由信息必须通

    过骨干区域来转发。对此,OSPF 有两个规定:

    所有非骨干区域必须与骨干区域保持连通;

    骨干区域自身也必须保持连通。

    在实际应用中,可能会因为各方面条件的限制,无法满足上面的要求。这时可以通过配置 OSPF 虚

    连接予以解决。

    2) 虚连接(Virtual Link)

    虚连接是指在两台 ABR 之间通过一个非骨干区域而建立的一条逻辑上的连接通道。它的两端必须

    是 ABR,而且必须在两端同时配置方可生效。为虚连接两端提供一条非骨干区域内部路由的区域称

    为传输区(Transit Area)。

    3、Stub区域和Totally Stub区域

    Stub 区域是一些特定的区域,该区域的 ABR 会将区域间的路由信息传递到本区域,但不会引入自

    治系统外部路由,区域中路由器的路由表规模以及 LSA 数量都会大大减少。为保证到自治系统外的

    路由依旧可达,该区域的 ABR 将生成一条缺省路由 Type-3 LSA,发布给本区域中的其他非 ABR

    路由器。

    为了进一步减少 Stub 区域中路由器的路由表规模以及 LSA 数量,可以将区域配置为 Totally Stub

    (完全 Stub)区域,该区域的 ABR 不会将区域间的路由信息和自治系统外部路由信息传递到本区

    域。为保证到本自治系统的其他区域和自治系统外的路由依旧可达,该区域的 ABR 将生成一条缺

    省路由 Type-3 LSA,发布给本区域中的其他非 ABR 路由器。

    4. NSSA区域和Totally NSSA区域

    NSSA(Not-So-Stubby Area)区域是 Stub 区域的变形,与 Stub 区域的区别在于 NSSA 区域允许

    引入自治系统外部路由,由 ASBR 发布 Type-7 LSA 通告给本区域。当 Type-7 LSA 到达 NSSA 的

    ABR 时,由 ABR 将 Type-7 LSA 转换成 Type-5 LSA,传播到其他区域。

    可以将区域配置为 Totally NSSA(完全 NSSA)区域,该区域的 ABR 不会将区域间的路由信息传

    递到本区域。为保证到本自治系统的其他区域的路由依旧可达,该区域的 ABR 将生成一条缺省路

    由 Type-3 LSA,发布给本区域中的其他非 ABR 路由器

    展开全文
  • 在研究袁店一矿地质条件的基础上,利用几何分形理论绘制岩体结构分布图,进而对103采区底板岩体结构的类型进行划分。结果表明:采区内大部分的岩体结构都是破碎的或是块裂的,只有部分区域岩体结构属于松散或是完整的。
  • 子网划分详解与子网划分实例精析

    万次阅读 多人点赞 2018-03-01 17:40:17
    子网划分理论基础 为什么进行子网划分 明确需求 知识点 子网划分常见问题 子网划分实例精析 C类子网划分实例分析 已知网络地址和子网掩码,求子网划分结果 已知IP地址和子网掩码求子网划分 B类地址...

    目录

    子网划分理论基础

    为什么进行子网划分

    • 减少网络流量,无论什么样的流量,我们都希望它少些,网络流量亦如此。如果没有可信赖的路由器,网络流量可能导致整个网络停顿,但有了路由器后,大部分流量都将呆在本地网络内,只有前往其他网络的分组将穿越路由器。路由器增加广播域,广播域越多,每个广播轻松划分子网域就越小,而每个网段的网络流量也越少。

    • 优化网络性能,这是减少网络流量的结果。

    • 简化管理,与庞大的网络相比,在一系列相连的小网络中找出并隔离网络问题更容易。

    • 有助于覆盖大型地理区域,WAN链路比LAN链路的速度慢得多,且更昂贵;单个大跨度的大型网络在前面说的各个方面都可能出现问题,而将多个小网络连接起来可提高系统的效率。
      ##明确需求
      我们创建子网的时候,一定是根据一定的要求创建的,这个要求就是我们实际的网络需求。一般我们按如下步骤来明确我们的网络需求:
      (1)确定需要的网络ID数:

    • 每个LAN子网一个;

    • 每条广域网连接一个。

    (2)确定每个子网所需的主机数:

    • 每个TCPIIP主机一个;
    • 每个路由器接口一个。

    (3)根据上述需求,确定如下内容:

    • 一个用于整个网络的子网掩码;
    • 每个物理网段的唯一子网ID;
    • 每个子网的主机范围。

    知识点

    • ip地址:我们需要知道网络地址分为A,B,C三类,并且知道ip地址是由网段号(net_id)+主机号(host_id)组成的.想要详细了解ip地址可以参考我这篇博文:为什么百度查到的ip和ipconfig查到的不一样;详解公网Ip和私网ip;详解网络分类ABC;

    • 子网掩码:要让子网划分方案管用,网络中的每台机器都必须知道主机地址的哪部分为子网地址,这是通过给每台机器分配子网掩码实现的。子网掩码是一个长32位的值,让IP分组的接收方能够将IP地址的网络ID部分和主机ID部分区分开来。网络管理员创建由1和0组成的32位子网掩码,其中的1表示lP地址的相应部分为网络地址或子网地址。

    并非所有网络都需要子网,这意味着网络可使用默认子网掩码。这相当于说IP地址不包含子网地址。下表列出了A类、B类和C类网络的默认子网掩码。
    这里写图片描述

    • CIDR:( Classless Inter-Domain Routing,元类域间路由选择).我们需要了解这种网络表示法。形式如:192.168.10.32/28。前面的数字是我们的网络地址,后面的28表示用28位来表示网络位,用32-28=4位来表示主机位。通过这种记法,我们能明确两个信息:
      • 网络地址:192.168.10.32
      • 子网掩码:255.255.255.240

    通过下表我们能明确子网掩码和斜杠表示法之间的关系
    这里写图片描述
    这里写图片描述
    其中/8-/15只能用于A类网络,/16-/23可用于A类和B类网络,而/24-/30可用于A类、B类和C类网络。这就是大多数公司都使用A类网络地址的一大原因,因为它们可使用所有的子网掩码,进行网络设计时的灵活性最大。

    子网划分常见问题

    • 选定的子网掩码将创建多少个子网?
      2^x个,其中x是子网掩码借用的主机位数。如:192.168.10.32/28,我们知道C类ip的默认子网掩码为:255.255.255.0,而由上文的CIDR知识,我们了解到这个ip的实际子网掩码是:255.255.255.240。原本最后一个字节应该是0(00000000),现在却是240(11110000)。故其借用了主机位4位来充当网络位。
      ** 这个地方不懂的话可以结合我后面的实际案例一起来看**
    • 每个子网可包含多少台主机?
      2^y-2台,其中y是没被借用的主机位的位数。-2是因为,主机位全为0的部分是这个子网的网段号(Net_id),全为1的部分是这个网段的广播地址。
    • 有哪些合法的子网?
      算出子网的步长(增量)。一个例子是256-192 = 64,即子网掩码为192时,步长为64。从0开始不断增加剧,直到到达子网掩码值,中间的结果就是子网,即0、64、128和192,
    • 每个子网的广播地址是什么?
      主机位全为1就是该子网的广播地址。一般我们这样计算:广播地址总是下一个子网前面的数.前面确定了子网为0、64、128和192,例如,子网0的广播地址为63,因为下一个子网为64;子网64的广播地址为127,因为下一个子网为128,以此类推。请记住,最后一个子网的广播地址总是255
    • 每个子网可包含哪些主机地址?
      合法的主机地址位于两个子网之间,但全为0和全为1的地址除外。例如,如果子网号(网段号)为64,而广播地址为127,则合法的主机地址范围为65-126,即子网地址和广播地址之间的数字。

    子网划分实例精析

    C类子网划分实例分析

    首先我们要知道C类可使用的全部子网掩码:
    这里写图片描述

    已知网络地址和子网掩码,求子网划分结果

    • 案例一:
      255.255.255.128 (/25)
      128的二进制表示为10000000,只有1位用于定义子网,余下7位用于定义主机。这里将对C类网络192.168.10.0进行子网划分。
      网络地址=192.168.10.0
      子网掩码=255.255.255.128
      回答五大问题:
      • 多少个子网?
        在128( 10000000 )中,取值为1的位数为1,借用了一位主机位,因此答案为2^1=2。
      • 每个子网多少台主机?
        有7个主机位取值为o( 10000000),还剩下7位主机位,因此答案是2^7-2= 126台主机。
      • 有哪些合法的子网?
        256 -128 = 128。也就是子网的增量是128.因此子网为0和128
      • 每个子网的广播地址是什么?
        在下一个子网之前的数字中,所有主机位的取值都为1,是当前子网的广播地址。对于子网0,下一个子网为128,因此其广播地址为127
      • 每个子网包含哪些合法的主机地址?
        合法的主机地址为子网地址和广播地址之间的数字。要确定主机地址,最简单的方法是写出子网地址和广播地址,这样合法的主机地址就显而易见了。

    下面我分别用图表和图画来表示该子网划分,以希望大家能有一个更深刻的理解。
    这里写图片描述
    这里写图片描述

    • 案例二:
      255.255.255.192 (/26)
      在第二个示例中,我们将使用子网掩码255.255.255.192对网络192.168.10.0进行子网划分。
      网络地址=192.168.10.0
      子网掩码=255.255.255.192
      下面来回答五大问题
      • 多少个子网?
        在192(11000000)中,取值为1的位数为2,因此答案为2^2=4个子网。
      • 每个子网多少台主机?有6个主机位的取值为o(11000000),因此答案是2^6-2=62台主机。
      • 有哪些合法的子网?
        256 -192 = 64。所以子网的步长[增量]为64,因此子网为0、64、128和192
      • 每个子网的广播地址是什么?
        在下一个子网之前的数字中,所有主机位的取值都为1,是当前子网的广播地址。对于子网0,下一个子网为64,因此其广播地址为63。以此类推。
      • 合法的主机地址有哪些?
        合法的主机地址为子网地址和广播地址之间的数字。要确定主机地址,最简单的方法是写出子网地址和广播地址,这样合法的主机地址就显而易见了。

    下面我分别使用图表和图画来更形象的展示这五大问题的答案。
    这里写图片描述
    这里写图片描述

    • 案例三:
      从这个案例开始,我不再一一回答这五大问题,大部分的思考是重复的,我只给出问题和图表类型的答案。
      255.255.255.224 (/27)
      这次我们将使用子网掩码255.255.255.224对网络192.168.10.0进行子网划分。
      网络地址=192.168.10.0
      子网掩码=255.255.255.224
      下表是图表类型的子网划分结果
      这里写图片描述

    • 案例四:
      255.255.255.240 (/28)
      再来看一个示例:
      网络地址=192.168.10.0
      子网掩码=255.255.255.240
      子网划分结果:
      这里写图片描述

    • 案例五:
      255.255.255.248 (/29)
      继续练习:
      网络地址=192.168.10.0
      子网掩码=255.255.255.248
      子网划分结果:
      这里写图片描述
      ###已知IP地址和子网掩码求子网划分
      **案例1: **
      已知ip地址=192.168.10.33 ,子网掩码=255.255.255.224,求该网络的子网划分。

    1. 求出子网增量:
      由于子网掩码是224,所以子网步长为256-224=32
    2. 求有哪些合法子网:
      由上文知道,子网的步长为32.因此子网为0、32、64等等
    3. 求出该Ip地址对应的子网号。
      因为主机地址33位于子网32和64之间,因此属于子网192.168.10.32
    4. 求该子网对应的广播地址:
      下一个子网为64,因此子网32的广播地址为63(广播地址总是下一个子网之前的数字)。
    5. 求合法的主机地址范围:
      33~62(子网和广播地址之间的数字)。

    案例2:
    ip地址=192.168.10.174
    子网掩码=255.255.255.240.合法的主机地址范围是多少呢?
    解答:子网掩码为240,因此将256减去240,结果为16,这是子网增量。要确定所属的子网,只需从零开始不断增加16,并在超过主机地址174后停止:0、16、32、48、64、80、96、112、128、144、160、176等。主机地址174位于160和176之间,因此所属的子网为160。广播地址为175,合法的主机地址范围为161~174。
    案例3:
    ip地址=192.168.10.17
    子网掩码=255.255.255.252 该IP地址属于哪个子网?该子网的广播地址是什么?
    解答:256 -252= 4,因此子网为0、4、8、12、16、20等(除非专门指出,否则总是从0开始)。主机地址17位于子网16和20之间,因此属于子网192.168.10.16,而该子网的广播地址为19,合法的主机地址范围为17-18。
    ##B类地址子网划分实例
    B类地址可使用的CIDR地址表:
    这里写图片描述
    注意:在B类地址中,有16位可用于主机地址。这意昧着最多可将其中的14位用于子网划分,因为至少需要保留2位用于主机编址。使用/16意味着不对B类网络进行子网划分,但它是一个可使用的子网掩码。

    已知网络地址和子网掩码求子网划分

    案例1:
    255.255.128.0 (/17)
    网络地址=172.16.0.0
    子网掩码=255.255.128.0

    • 多少个子网?
      2^1 =2 (与C类网络相同)借用了一位主机位。
    • 每个子网多少台主机?
      2^15 -2 = 32766 (主机位一共15位,第三个字节7位,第四个字节8位)。
    • 有哪些合法的子网?
      256 -128 = 128,因此子网为0和128。鉴于子网划分是在第三个字节中进行的,因此子网号实际上为0.0和128.0
    • 每个子网的广播地址是什么?(跟C类相同,广播地址总是下一个子网前面的数)
    • 合法的主机地址是什么?(子网号与广播地址之间的地址就是合法的主机地址)

    用图表来表示出上面的参数
    这里写图片描述
    案例2:
    255.255.255.128 (/25)
    这是一个非常难但是却十分适合生产环境的子网划分组合
    网络地址=172.16.0.0
    子网掩码=255.255.255.128

    • 多少个子网?
      2^9=512。一共借用了9个主机位
    • 每个子网多少台主机?
      2^7-2 = 126。 还有16-9=7位主机位
    • 有哪些合法的子网?
      这是比较棘手的部分。这个地方的子网增量应该是 256-255=1,因此第三个字节的可能取值为0、1 、2、3…255;但别忘了,第四个字节还有一个子网位。还记得前面如何在C类网络中处理只有一个子网位的情况吗?这里的处理方式相同。也就是说第三个字节的每个取值都有0和128这两种情况。例如,如果第三个字节的取值为3,则对应的两个子网为3.0和3.128。因此总共有512个子网。
    • 每个子网的广播地址是什么?(下一个子网地址的前一位)
    • 合法的主机地址是什么?(介于子网地址和该子网的广播地址之间的就是主机地址)
      下面用图表列出这个例子的子网划分结果:
      这里写图片描述

    已知ip地址和子网掩码求子网划分

    当使用cidr表示子网划分,网络位的位数>24时,比如/25,/27.我们只需要考虑第四个字节。<=24时,我们只需要考虑第三个字节,因为第四个字节的主机位并没有被借用,并没有参与到子网划分。

    • 问题:172.16.10.33/27属于哪个子网?该子网的广播地址是多少?
      答案:这里只需考虑第四个字节。256-224=32,故第四个字节的变化为0、32、64…。33位于32和64之间,但子网号还有一部分位于第三个字节,因此
      答案是该地址位于子网10.32中。由于下一个子网为10.64,该子网的广播地址为172.16.10.63
    • 问题:IP地址=172.16.66.10;子网掩码=255.255.192.0(/18)属于哪个子网?该子网的广播地址是多少?
      答案:这里需要考虑的是第三个字节,而不是第四个字节。256-192=64,因此子网为0.0、64.0、128.0等。所属的子网为172.16.64.0。由于下一个子网为128.0,该子网的广播地址为172.16.127.255。
    • 问题:IP地址=172.16.50.10;子网掩码=255.255.224.0(/19)属于哪个子网?该子网的广播地址是多少?
      答案:只需要考虑第三个字节。256-224=32,因此子网为0.0、32.0、64.0等(所属的子网为172.16.32.0,因而其广播地址为172.16.63.255,因为下一个子网为64.0。
    • 问题:IP地址=172.16.45.14;子网掩码=255.255.255.252(/30)属于哪个子网?该子网的广播地址是多少?
      答案:这里需要考虑哪个字节呢?第四个。256-252=4,因此子网为0、4、8、12、16等。所属的子网为172.16.45.12,而该子网的广播地址为172.16.45.15,因为下一个子网为172.16.45.16。
      ##A类子网划分实例
      A类网络的子网划分与B类和C类网络没有什么不同,但需要处理的是24位,而B类和C类网络中需处理的分别是16位和8位。
      可用于A类的所有子网掩码:
      这里写图片描述

    已知网络地址和子网掩码求子网划分

    案例1:
    255.255.240.0(/20)
    网络地址=10.0.0.0
    子网掩码=255.255.240.0(/20)时,12位用于子网划分,余下12位用于主机编址。

    • 多少个子网?
      2^12=4096。
    • 每个子网的主机数?
      2^12-2=4094
    • 有哪些合法的子网?
      需要考虑哪些字节?借用的主机号来自于第二和第三个字节,因此要考虑第二个和第三个字节,在第二个字节中,子网号的间隔为1;在第三个字节中,子网号为0、16、32等,因为256-240=160
    • 每个子网的广播地址是什么?
    • 合法的主机地址是什么?
      具体划分如表中所示:
      这里写图片描述

    案例2:
    网络地址=10.0.0.0
    子网掩码=255.255.255.192(/26)
    这个例子将第二个、第三个和第四个字节用于划分子网。

    • 多少个子网?
      2^18=262144。
    • 每个子网的主机数?
      2^6-2=62。
    • 有哪些合法的子网?
      在第二个和第三个字节中,子网号间隔为1,而在第四个字节中,子网号间隔为64
    • 每个子网的广播地址是什么?
    • 合法的主机地址是什么?

    下面只列出最后一部分的子网划分
    这里写图片描述

    已知ip地址和子网掩码求子网划分

    最后一个案例:
    ip地址=10.1.3.65/23
    求该ip地址对应的子网以及该子网合法的主机地址和广播地址:
    **回答:**首先,如果不知道/23对应的子网掩码,你就回答不了这个问题。它对应的子网掩码为255.255.254.0。这里需要注意的字节为第三个。256-254=2,因此第三个字节的子网号为0、2、4、6等。在这个问题中,主机位于子网2.0中,而下一个子网为4.0,因此该子网的广播地址为3.255。10.1.2.1~10.1.3.254中的任何地址都是该子网中合法的主机地址。
    #小结
    所有的子网划分都是基于C类作为基础的,如果我们能够突破c类这个难关就一定能看明白子网划分。另外,该博文当中大量实例来自于《CCNA学习指南一书》,如果有想深入了解计算机网络的同学可以下载此书:CCNA学习指南下载地址
    **ps:写博客不容易,转载请注明出处,by 小小呆 **

    展开全文
  • 以长三角地区为例,首先划分出农业主导、工业主导、服务主导和均衡发展4种乡村发展类型;基于区域城乡一体的思想构建乡村性指数RI理论公式,对2000 ~ 2012年长三角地区乡村性及演变特征进行了探讨.研究结果表明:长...
  • Java内存区域怎么划分的?

    万次阅读 2020-09-18 15:12:57
    Java内存区域怎么划分的? 运行时数据区域包含以下五个区域:程序计数器,Java虚拟机栈,本地方法栈,堆,方法区(其中前三个区域各线程私有,相互独立,后面两个区域所有线程共享) 线程私用的部分(Java虚拟机栈,...

    Java内存区域怎么划分的?

    运行时数据区域包含以下五个区域:程序计数器,Java虚拟机栈,本地方法栈,堆,方法区(其中前三个区域各线程私有,相互独立,后面两个区域所有线程共享) 

    线程私用的部分(Java虚拟机栈,本地方法栈,程序计数器)

    Java虚拟机栈

    执行一个Java方法时,虚拟机都会创建一个栈帧,来存储局部变量表,操作数栈等,方法调用完毕后会对栈帧从虚拟机栈中移除。

    局部变量表中存储了Java基本类型,对象引用(可以是对象的存储地址,也可以是代表对象的句柄等)和returnAddress类型(存储了一条字节码指令的地址)。

    本地方法栈

    本地方法栈与Java虚拟机栈类似,只不过是执行Native方法(C++方法等)。

    程序计数器

    计数器存储了当前线程正在执行的字节码指令的地址(如果是当前执行的是Native方法,那么计数器为空),字节码解释器就是通过改变计数器的值来选取下一条需要执行的字节码指令。程序计数器是线程私有的,便于各个线程切换后,可以恢复到正确的执行位置。

    线程共享的部分(堆,方法区)

    Java 堆

    堆存储了几乎所有对象实例和数组,是被所有线程进行共享的区域。在逻辑上是连续的,在物理上可以是不连续的内存空间(在存储一些类似于数组的这种大对象时,基于简单和性能考虑会使用连续的内存空间)。

    方法区

    存储了被虚拟机加载的类型信息,常量,静态变量等数据,在JDK8以后,存储在元空间中(以前是存储在堆中的永久代中,JDK8以后已经没有永久代了)。

    运行时常量池是方法区的一部分,会存储各种字面量和符号引用。具备动态性,运行时也可以添加新的常量入池(例如调用String的intern()方法时,如果常量池没有相应的字符串,会将它添加到常量池)。

    直接内存区(不属于虚拟机运行时数据区)

    直接内存区不属于虚拟机运行时数据区的一部分。它指的是使用Native方法直接分配堆外内存,然后通过Java堆中的DirectByteBuffer来对内存的引用进行操作(可以避免Java堆与Native堆之间的数据复制,提升性能)。

    Java中对象的创建过程是怎么样的?

    这是网上看到的一张流程图:

    java对象创建流程

    1.类加载检查

    首先代码中new关键字在编译后,会生成一条字节码new指令,当虚拟机遇到一条字节码new指令时,会根据类名去方法区运行时常量池找类的符号引用,检查符号引用代表的类是否已加载,解析和初始化过。如果没有就执行相应的类加载过程。

    2.分配内存

    虚拟机从Java堆中分配一块大小确定的内存(因为类加载时,创建一个此类的实例对象的所需的内存大小就确定了),并且初始化为零值。内存分配的方式有指针碰撞空闲列表两种,取决于虚拟机采用的垃圾回收期是否带有空间压缩整理的功能。

    指针碰撞

    如果垃圾收集器是Serial,ParNew等带有空间压缩整理的功能时,Java堆是规整的,此时通过移动内存分界点的指针,就可以分配空闲内存。

    空闲列表

    如果垃圾收集器是CMS这种基于清除算法的收集器时,Java堆中的空闲内存和已使用内存是相互交错的,虚拟机会维护一个列表,记录哪些可用,哪些不可用,分配时从表中找到一块足够大的空闲内存分配给实例对象,并且更新表。

    3.对象初始化(虚拟机层面)

    虚拟机会对对象进行必要的设置,将对象的一些信息存储在Obeject header 中。

    4.对象初始化(Java程序层面)

    在构造一个类的实例对象时,遵循的原则是先静后动,先父后子,先变量,后代码块,构造器。在Java程序层面会依次进行以下操作:

    • 初始化父类的静态变量(如果是首次使用此类)

    • 初始化子类的静态变量(如果是首次使用此类)

    • 执行父类的静态代码块(如果是首次使用此类)

    • 执行子类的静态代码块(如果是首次使用此类)

    • 初始化父类的实例变量

    • 初始化子类的实例变量

    • 执行父类的普通代码块

    • 执行子类的普通代码块

    • 执行父类的构造器

    • 执行子类的构造器

    PS:如何解决内存分配时的多线程并发竞争问题?

    内存分配不是一个线程安全的操作,在多个线程进行内存分配是,可能会存在数据不同步的问题。所以有两种方法解决:

    添加CAS锁

    对内存分配的操作进行同步处理,添加CAS锁,配上失败重试的方式来保证原子性。(默认使用这种方式)。

    预先给各线程分配TLAB

    预先在Java堆中给各个线程分配一块TLAB(本地线程缓冲区)内存,每个线程先在各自的缓冲区中分配内存,使用完了再通过第一种添加CAS锁的方式来分配内存。(是否启动取决于-XX:+/-UseTLAB参数)。

    Java对象的内存布局是怎么样的?

    对象在内存中存储布局主要分为对象头,实例数据和对齐填充三部分。

    Serial收集器中,新生代与老年代的内存分配是1:2,然后新生代分为Eden区,From区,To区,比例是8:1:1。

    新生代

    分为Eden,From Survivor,To Survivor,8:1:1

    Eden用来分配新对象,满了时会触发Minor GC。

    From Survivor是上次Minor GC后存活的对象。

    To Survivor是用于下次Minor GC时存放存活的对象。

    老年代

    用于存放存活时间比较长的对象,大的对象,当容量满时会触发Major GC(Full GC)

    内存分配策略:

    1. 对象优先在Eden分配

    当Eden区没有足够空间进行分配时,虚拟机将发起一次MinorGC。现在的商业虚拟机一般都采用复制算法来回收新生代,将内存分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor。 当进行垃圾回收时,将Eden和Survivor中还存活的对象一次性地复制到另外一块Survivor空间上,最后处理掉Eden和刚才的Survivor空间。(HotSpot虚拟机默认Eden和Survivor的大小比例是8:1)当Survivor空间不够用时,需要依赖老年代进行分配担保。

    1. 大对象直接进入老年代

    所谓的大对象是指,需要大量连续内存空间的Java对象,最典型的大对象就是那种很长的字符串以及数组,为了避免大对象在Eden和两个Survivor区之间进行来回复制,所以当对象超过-XX:+PrintTenuringDistribution参数设置的大小时,直接从老年代分配

    1. 长期存活的对象将进入老年代。

    当对象在新生代中经历过一定次数(XX:MaxTenuringThreshold参数设置的次数,默认为15)的Minor GC后,就会被晋升到老年代中。

    1. 动态对象年龄判定。

    为了更好地适应不同程序的内存状况,虚拟机并不是永远地要求对象年龄必须达到了MaxTenuringThreshold才能晋升老年代,如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。

    MinorGC和FullGC是什么?

    Minor GC:对新生代进行回收,不会影响到年老代。因为新生代的 Java 对象大多死亡频繁,所以 Minor GC 非常频繁,一般在这里使用速度快、效率高的算法,使垃圾回收能尽快完成。

    Full GC:也叫 Major GC,对整个堆进行回收,包括新生代和老年代。由于Full GC需要对整个堆进行回收,所以比Minor GC要慢,因此应该尽可能减少Full GC的次数,导致Full GC的原因包括:老年代被写满和System.gc()被显式调用等。

    触发Minor GC的条件有哪些?

    1.为新对象分配内存时,新生代的Eden区空间不足。 新生代回收日志:

    2020-05-12T16:15:10.736+0800: 7.803: [GC (Allocation Failure) 2020-05-12T16:15:10.736+0800: 7.803: [ParNew: 838912K->22016K(943744K), 0.0982676 secs] 838912K->22016K(1992320K), 0.0983280 secs] [Times: user=0.19 sys=0.01, real=0.10 secs]
    

    触发Full GC的条件有哪些?

    主要分为三种:

    1.system.gc()

    代码中调用system.gc()方法,建议JVM进行垃圾回收。

    2.方法区空间不足

    方法区中存放的是一些类的信息,当系统中要加载的类、反射的类和调用的方法较多时,方法区可能会被占满,触发 Full GC

    3.老年代空间不足

    而老年代空间不足又有很多种情况: 3.1 Promotion Failed 老年代存放不下晋升对象 在进行 MinorGC 时, Survivor Space 放不下存活的对象,此时会让这些对象晋升,只能将它们放入老年代,而此时老年代也放不下时造成的。 还有一些情况也会导致新生代对象晋升,例如存活对象经历的垃圾回收次数超过一定次数(XX:MaxTenuringThreshold参数设置的次数,默认为15),那么会导致晋升, 或者在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。 3.2 Concurrent Mode Failure 在执行 CMS GC 的过程中,同时有对象要放入老年代,而此时老年代空间不足造成的。 3.3 历次晋升的对象平均大小>老年代的剩余空间 这是一个较为复杂的触发情况, HotSpot为了避免由于新生代对象晋升到老年代导致老年代空间不足的现象, 在进行 Minor GC时,做了一个判断,如果之前统计所得到的 MinorGC 晋升到老年代的平均大小大于老年代的剩余空间,那么就直接触发 Full GC。 3.4 老年代空间不足以为大对象分配内存 因为超过阀值(-XX:+PrintTenuringDistribution参数设置的大小时)的大对象,会直接分配到老年代,如果老年代空间不足,会触发Full GC。

    垃圾收集器有哪些?

    一般老年代使用的就是标记-整理,或者标记-清除+标记-整理结合(例如CMS)

    新生代就是标记-复制算法

    垃圾收集器特点算法适用内存区域
    Serial单个GC线程进行垃圾回收,简单高效标记-复制新生代
    Serial Old单个GC线程进行垃圾回收标记-整理老年代
    ParNew是Serial的改进版,就是可以多个GC线程一起进行垃圾回收标记-复制新生代
    Parallel Scanvenge收集器(吞吐量优先收集器)高吞吐量,吞吐量=执行用户线程的时间/CPU执行总时间标记-复制新生代
    Parallel Old收集器支持多线程收集标记-整理老年代
    CMS收集器(并发低停顿收集器)低停顿标记-清除+标记-整理老年代
    G1收集器低停顿,高吞吐量标记-复制算法老年代,新生代

    Serial收集器(标记-复制算法)

    就是最简单的垃圾收集器,也是目前 JVM 在 Client 模式默认的垃圾收集器,在进行垃圾收集时会停止用户线程,然后使用一个收集线程进行垃圾收集。主要用于新生代,使用标记-复制算法。

    优点是简单高效(与其他收集器的单线程比),内存占用小,因为垃圾回收时就暂停所有用户线程,然后使用一个单线程进行垃圾回收,不用进行线程切换。

    缺点是收集时必须停止其他用户线程。

    Serial Old收集器(标记-整理算法)

    跟Serial收集器一样,不过是应用于老年代,使用标记-整理算法。

    ParNew收集器(标记-复制算法)

    ParNew收集器是Serial收集器的多线程并行版本,在进行垃圾收集时可以使用多个线程进行垃圾收集。

    与Serial收集器主要区别就是支持多线程收集,ParNew收集器应用广泛(JDK9以前,服务端模式垃圾收集组合官方推荐的是ParNew+CMS),因为只有Serial和ParNew才能配合CMS收集器(应用于老年代的并发收集器)一起工作。

    image-20200228185412716.pngimage-20200228185412716

    Parallel Scanvenge收集器(吞吐量优先收集器)

    也支持多线程收集,它的目标是达到一个可控制的吞吐量,就是运行用户代码的时间/CPU消耗的总时间的比值。高吞吐量可以最高效率地利用处理器资源,尽快完成程序运算任务,适合不需要太多的交互分析任务。不支持并发收集,进行垃圾回收时会暂停用户线程,使用多个垃圾回收线程进行垃圾回收。

    Parallel Old收集器

    是Parallel Scanvenge老年代版本,支持多线程收集,使用标记整理法实现的,

      

    G1对象分配策略

    说起对象的分配,我们不得不谈谈对象的分配策略。它分为4个阶段:

    1. 栈上分配
    2. TLAB(Thread Local Allocation Buffer)线程本地分配缓冲区
    3. 共享Eden区中分配
    4. Humongous区分配(超过Region大小50%的对象)

    对象在分配之前会做逃逸分析,如果该对象只会被本线程使用,那么就将该对象在栈上分配。这样对象可以在函数调用后销毁,减轻堆的压力,避免不必要的gc。 如果对象在栈是上分配不成功,就会使用TLAB来分配。TLAB为线程本地分配缓冲区,它的目的为了使对象尽可能快的分配出来。如果对象在一个共享的空间中分配,我们需要采用一些同步机制来管理这些空间内的空闲空间指针。在Eden空间中,每一个线程都有一个固定的分区用于分配对象,即一个TLAB。分配对象时,线程之间不再需要进行任何的同步。

    对TLAB空间中无法分配的对象,JVM会尝试在共享Eden空间中进行分配。如果是大对象,则直接在Humongous区分配。

    最后,G1提供了两种GC模式,Young GC和Mixed GC,两种都是Stop The World(STW)的。下面我们将分别介绍一下这2种模式。

    G1 Young GC

    Young GC主要是对Eden区进行GC,它在Eden空间耗尽时会被触发。在这种情况下,Eden空间的数据移动到Survivor空间中,如果Survivor空间不够,Eden空间的部分数据会直接晋升到年老代空间。Survivor区的数据移动到新的Survivor区中,也有部分数据晋升到老年代空间中。最终Eden空间的数据为空,GC停止工作,应用线程继续执行。

    Region如何解决跨代指针?

    因为老年代old区也会存在对新生代Eden区的引用,如果只是为了收集Eden区而对整个老年代进行扫描,那样开销太大了,所以G1其实会将每个Region分为很多个区,每个区有一个下标,当这个区有对象被其他Region引用时,那么CardTable对应下标下值为0,然后使用一个Rset来存储其他Region对当前Region的引用,Key是别的Region的起始地址,Value是一个集合,里面的元素是Card Table的Index。对跨代引用的扫描,只需要扫描RSet就行了。

    在CMS中,也有RSet的概念,在老年代中有一块区域用来记录指向新生代的引用。这是一种point-out,在进行Young GC时,扫描根时,仅仅需要扫描这一块区域,而不需要扫描整个老年代。

    但在G1中,并没有使用point-out(就是记录当前Region对其他Region中对象的引用),这是由于一个Region太小,Region数量太多,如果是用point-out的话,如果需要计算一个Region的可回收的对象数量,需要把所有Region都是扫描一遍会造成大量的扫描浪费,有些根本不需要GC的分区引用也扫描了。于是G1中使用point-in来解决。point-in的意思是哪些Region引用了当前Region中的对象。这样只需要将当前Region中这些对象当做初始标记时的根对象来扫描就可以扫描出因为有跨代引用需要存活的对象,避免了无效的扫描。

    由于新生代有多个,那么我们需要在新生代之间记录引用吗?这是不必要的,原因在于每次GC时,所有新生代都会被扫描,所以只需要记录老年代的Region对新生代的这个Region之间的引用即可。

    需要注意的是,如果引用的对象很多,赋值器需要对每个引用做处理,赋值器开销会很大,为了解决赋值器开销这个问题,在G1 中又引入了另外一个概念,卡表(Card Table)。一个Card Table将一个分区在逻辑上划分为固定大小的连续区域,每个区域称之为卡。卡通常较小,介于128到512字节之间。CardTable通常为字节数组,由Card的索引(即数组下标)来标识每个分区的空间地址。默认情况下,每个卡都未引用。当一个地址空间有引用时,这个地址空间对应的数组索引的值被标记为"0",即标记为脏引用,此外RSet也将这个数组下标记录下来。一般情况下,这个RSet其实是一个Hash Table集合(每个线程对应一个Hash Table,主要是为了减少多线程并发更新RSet的竞争),每个哈希表的Key是别的Region的起始地址,Value是一个集合,里面的元素是Card Table的Index。

    如果Rset是记录每个外来Region对当前Region中对象的引用,这样数量就太多了,所以Card Table只是有很多Byte字节,每个字节记录了Region对应的一个内存区域(卡页)是否是dirty的,为1代表dirty,也就是有其他Region对这个卡页中的对象进行引用。

     

    • 阶段1:根扫描 表态和本地对象被扫描
    • 阶段2:更新RS 处理dirty card队列更新RS
    • 阶段3:处理RS 检测从新生代指向老年代的对象
    • 阶段4:对象拷贝 拷贝存活的对象到survivor/old区域
    • 阶段5:处理引用队列 软引用、弱引用、虚引用处理

    G1 MixGC

    MixGC不仅进行正常的新生代垃圾收集,同时也回收部分后台扫描线程标记的老年代分区。Young GC回收是把新生代活着的对象都拷贝到Survivor的特定区域(Survivor to),剩下的Eden和Survivor from就可以全部回收清理了。那么,mixed GC就是把一部分老年区的region加到Eden和Survivor from的后面,合起来称为collection set, 就是将被回收的集合,下次mixed GC evacuation把他们所有都一并清理。选old region的顺序是垃圾多的(存活对象少)优先。

    它的GC步骤分2步:

    1. 全局并发标记(global concurrent marking)
    2. 拷贝存活对象(evacuation)

    在进行Mix GC之前,会先进行global concurrent marking(全局并发标记)。 global concurrent marking的执行过程是怎样的呢?

    在G1 GC中,它主要是为Mixed GC提供标记服务的,并不是一次GC过程的一个必须环节。global concurrent marking的执行过程分为五个步骤:

    • 初始标记(initial mark,STW) 在此阶段,G1 GC 对根进行标记。该阶段与常规的(STW) 新生代垃圾回收密切相关。
    • 根区域扫描(root region scan) G1 GC 在初始标记的存活区扫描对老年代的引用,并标记被引用的对象。该阶段与应用程序(非 STW)同时运行,并且只有完成该阶段后,才能开始下一次 STW 新生代垃圾回收。
    • 并发标记(Concurrent Marking) G1 GC 在整个堆中查找可访问的(存活的)对象。该阶段与应用程序同时运行,可以被 STW 新生代垃圾回收中断
    • 最终标记(Remark,STW) 该阶段是 STW 回收,帮助完成标记周期。G1 GC清空 SATB 缓冲区,跟踪未被访问的存活对象,并执行引用处理。
    • 清除垃圾(Cleanup,STW) 在这个最后阶段,G1 GC 执行统计和 RSet 净化的 STW 操作。在统计期间,G1 GC 会识别完全空闲的区域和可供进行混合垃圾回收的区域。清理阶段在将空白区域重置并返回到空闲列表时为部分并发。

    收集步骤:

    image-20200302181639409.pngimage-20200302181639409

    初始标记 只标记GC Roots直接引用的对象

    并发标记 从GC Roots开始对堆中对象进行可达性分析,扫描整个对象图,找出要回收的对象。(可以与用户线程并发执行)

    最终标记 对并发标记阶段,由于用户线程执行造成的改动进行修正,使用原始快照方法。

    筛选回收 对Region进行排序,根据回收价值,选择任意多个Region构成回收集,将存活对象复制到空的Region中去,因为涉及到存活对象的移动,所以是暂停用户线程的。

    垃圾收集器相关的参数

    -XX:+UseSerialGC, 虚拟机运行在Client 模式下的默认值,打开此开关后,使用Serial + Serial Old 的收集器组合进行内存回收

    -XX:+UseConcMarkSweepGC,打开此开关后,使用ParNew + CMS + Serial Old 的收集器组合进行内存回收。Serial Old 收集器将作为CMS 收集器出现Concurrent Mode Failure失败后的后备收集器使用。(我们的线上服务用的都是这个)

    -XX:+UseParallelGC,虚拟机运行在Server 模式下的默认值,打开此开关后,使用Parallel Scavenge + Serial Old(PS MarkSweep)的收集器组合进行内存回收。

    -XX:+UseParallelOldGC,打开此开关后,使用Parallel Scavenge + Parallel Old 的收集器组合进行内存回收。

    -XX:+UseG1GC,打开此开关后,采用 Garbage First (G1) 收集器

    -XX:+UseParNewGC,在JDK1.8被废弃,在JDK1.7还可以使用。打开此开关后,使用ParNew + Serial Old 的收集器组合进行内存回收

    目前通常使用的是什么垃圾收集器?

    怎么查询当前JVM使用的垃圾收集器?

    使用这个命令可以查询当前使用的垃圾收集器 java -XX:+PrintCommandLineFlags -version,

    另外这个命令可以查询到更加详细的信息

    java -XX:+PrintFlagsFinal -version | grep GC

    我们在IDEA中启动的一个Springboot的项目,默认使用的垃圾收集器参数是 -XX:+UseParallelGC

    -XX:InitialHeapSize=134217728 -XX:MaxHeapSize=2147483648 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC 
    java version "1.8.0_73"
    Java(TM) SE Runtime Environment (build 1.8.0_73-b02)
    Java HotSpot(TM) 64-Bit Server VM (build 25.73-b02, mixed mode)
    

    Parallel Scavenge+Serial Old

    JDK8默认情况下服务端模式下JVM垃圾回收参数是-XX:+UseParallelGC参数,也就是会使用Parallel Scavenge+Serial Old的收集器组合,进行内存回收。

    ParNew+CMS

    但是一般如果我们的后端应用不是那种需要进行大量计算的应用,基于低延迟的考虑,可以考虑使用-XX:+UseConcMarkSweepGC进行垃圾收集,这种配置下会使用ParNew来收集新生代内存,CMS垃圾回收器收集老年代内存。

    G1

    在JDK9时,默认的垃圾收集器是G1收集器,也可以使用-XX:+UseG1GC参数来启动G1垃圾收集器。

     

    容器的内存和 jvm 的内存有什么关系?参数怎么配置?

    在JDK8以后,JVM增加了容器感知功能,就是如果不显示指定-Xmx2048m 最大堆内存大小, -Xms2048m最小堆内存大小,会取容器所在的物理机的内存的25%作为最大堆内存大小, 也可以通过这几个参数来设置堆内存占容器内存的比例 -XX:MinRAMPercentage -XX:MaxRAMPercentage -XX:InitialRAMPercentage

    如何大体估算java进程使用的内存呢?

    Max memory = [-Xmx] + [-XX:MaxPermSize] + number_of_threads * [-Xss]

    -Xss128k:设置每个线程的堆栈大小.JDK5.0以后每个线程的栈大小为1M。

    -Xms 堆内存的初始大小,默认为物理内存的1/64 -Xmx 堆内存的最大大小,默认为物理内存的1/4 -Xmn 堆内新生代的大小。通过这个值也可以得到老生代的大小:-Xmx减去-Xmn

    -Xss 设置每个线程可使用的内存大小,即栈的大小。在相同物理内存下,减小这个值能生成更多的线程,当然操作系统对一个进程内的线程数还是有限制的,不能无限生成。线程栈的大小是个双刃剑,如果设置过小,可能会出现栈溢出,特别是在该线程内有递归、大的循环时出现溢出的可能性更大,如果该值设置过大,就有影响到创建栈的数量,如果是多线程的应用,就会出现内存溢出的错误。

    怎么获取 dump 文件?怎么分析?

    1. 启动时配置,出现OOM问题时自动生成 JVM启动时增加两个参数:
    //出现 OOME 时生成堆 dump: 
    -XX:+HeapDumpOnOutOfMemoryError
    //生成堆文件地址:
    -XX:HeapDumpPath=/home/liuke/jvmlogs/
    

    2.执行jmap命令立即生成 发现程序异常前通过执行指令,直接生成当前JVM的dmp文件,6214是指JVM的进程号

    jmap -dump:format=b,file=/home/admin/logs/heap.hprof 6214
    

    获得heap.hprof以后,执行jvisualvm命令打开使用Java自带的工具Java VisualVM来打开heap.hprof文件,就可以分析你的Java线程里面对象占用堆内存的情况了

    由于第一种方式是一种事后方式,需要等待当前JVM出现问题后才能生成dmp文件,实时性不高,第二种方式在执行时,JVM是暂停服务的,所以对线上的运行会产生影响。所以建议第一种方式。

    gc日志怎么看?

    这是一条Minor GC的回收日志

    2020-05-07T16:28:02.845+0800: 78210.469: [GC (Allocation Failure) 2020-05-07T16:28:02.845+0800: 78210.469: [ParNew: 68553K->466K(76672K), 0.0221963 secs] 131148K->63062K(2088640K), 0.0223082 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] 
    

    GC (Allocation Failure)

    代表Eden区分配内存失败触发Minor GC。 2020-05-07T16:28:02.845+0800: 78210.469

    是发生的时间。 68553K->466K(76672K)

    代表垃圾回收前新生代使用内存是68MB,剩余0.4MB,总内存是76MB。 0.0221963 secs

    是垃圾回收耗时。 131148K->63062K(2088640K)

    代表堆区回收前使用131MB,63MB,总内存是2088MB。

    [Times: user=0.02 sys=0.00, real=0.02 secs]

    用户态耗时0.02s,内核态耗时0s,总耗时0.02s

    cpu 使用率特别高,怎么排查?通用方法?定位代码?cpu高的原因?

    CPU飙高,频繁GC,怎么排查?

    jstack命令:教你如何排查多线程问题

     jstat -gcutil 29530 1000 10
     垃圾回收信息统计,29530是pid,1000是每1秒打印一次最新信息,10是最多打印10次
    

    怎么排查CPU占用率过高的问题?

    1.首先使用top命令查看CPU占用率高的进程的pid。

    top - 15:10:32 up 523 days,  3:47,  1 user,  load average: 0.00, 0.01, 0.05
    Tasks:  95 total,   1 running,  94 sleeping,   0 stopped,   0 zombie
    %Cpu(s):  1.7 us,  0.5 sy,  0.0 ni, 95.7 id,  2.2 wa,  0.0 hi,  0.0 si,  0.0 st
    KiB Mem : 16267904 total,  6940648 free,  2025316 used,  7301940 buff/cache
    KiB Swap: 16777212 total, 16776604 free,      608 used. 13312484 avail Mem
    
      PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
    14103 hadoop    20   0 2832812 203724  18392 S   3.7  1.3 977:08.04 java
    14010 hadoop    20   0 2897344 285392  18660 S   0.3  1.8 513:30.49 java
    14284 hadoop    20   0 3052556 340436  18636 S   0.3  2.1   1584:47 java
    14393 hadoop    20   0 2912460 504112  18632 S   0.3  3.1 506:43.68 java
        1 root      20   0  190676   3404   2084 S   0.0  0.0   4:31.47 systemd
        2 root      20   0       0      0      0 S   0.0  0.0   0:04.77 kthreadd
        3 root      20   0       0      0      0 S   0.0  0.0   0:10.16 ksoftirqd/0
    

    2.使用top -Hp 进程id获得该进程下各个线程的CPU占用情况,找到占用率最高的线程的pid2, 使用printf "%x\n" pid2命令将pid2转换为16进制的数number。

    top - 15:11:01 up 523 days,  3:48,  1 user,  load average: 0.00, 0.01, 0.05
    Threads:  69 total,   0 running,  69 sleeping,   0 stopped,   0 zombie
    %Cpu(s): 12.8 us,  0.1 sy,  0.0 ni, 87.0 id,  0.1 wa,  0.0 hi,  0.0 si,  0.0 st
    KiB Mem : 16267904 total,  6941352 free,  2024612 used,  7301940 buff/cache
    KiB Swap: 16777212 total, 16776604 free,      608 used. 13313188 avail Mem
    
      PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
    14393 hadoop    20   0 2912460 504112  18632 S  0.0  3.1   0:00.01 java
    14411 hadoop    20   0 2912460 504112  18632 S  0.0  3.1   0:01.95 java
    14412 hadoop    20   0 2912460 504112  18632 S  0.0  3.1   0:16.18 java
    14413 hadoop    20   0 2912460 504112  18632 S  0.0  3.1   0:12.79 java
    14414 hadoop    20   0 2912460 504112  18632 S  0.0  3.1   8:09.10 java
    

    3.使用jstack pid获得进程下各线程的堆栈信息,nid=0xnumber的线程即为占用率高的线程,查看它是在执行什么操作。(jstack 5521 | grep -20 0x1596可以获得堆栈信息中,会打印匹配到0x1596的上下20行的信息。)

    例如这个线程是在执行垃圾回收:

    "GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007f338c01f000 nid=0x1593 runnable
    

    JVM相关的异常

    1.stackoverflow

    这种就是栈的空间不足,就会抛出这个异常,一般是递归执行一个方法时,执行方法深度太深时出现。Java执行一个方法时,会创建一个栈帧来存放局部变量表,操作数栈,如果分配栈帧时,栈空间不足,那么就会抛出这个异常。

    (栈空间可以设置-Xss参数实现,默认为1M,如果参数)

    双亲委派机制是什么?

     

    就是类加载器一共有三种:

    启动类加载器:主要是在加载JAVA_HOME/lib目录下的特定名称jar包,例如rt.jar包,像java.lang就在这个jar包中。

    扩展加载器:主要是加载JAVA_HOME/lib/ext目录下的具备通用性的类库。

    应用程序加载器:加载用户类路径下所有的类库,也就是程序中默认的类加载器。

    工作流程:

    除启动类加载器以外,所有类加载器都有自己的父类加载器,类加载器收到一个类加载请求时,首先会判断类是否已经加载过了,没有的话会调用父类加载器的的loadClass方法,将请求委派为父加载器,当父类加载器无法完成类加载请求时,子加载器才尝试去加载这个类。 目的是为了保证每个类只加载一次,并且是由特定的类加载器进行加载(都是首先让启动类来进行加载)。

    public abstract class ClassLoader {
        ...
        public Class<?> loadClass(String name) throws ClassNotFoundException {
            return loadClass(name, false);
        }
        protected Class<?> loadClass(String name, boolean resolve)
            throws ClassNotFoundException {
            synchronized (getClassLoadingLock(name)) {
                Class<?> c = findLoadedClass(name);
                if (c == null) {
                    ...
                    try {
                        if (parent != null) {
                            c = parent.loadClass(name, false);
                        } else {
                            c = findBootstrapClassOrNull(name);
                        }
                    } catch (ClassNotFoundException e) {
                    }
    
                    if (c == null) {
                        ...
                        c = findClass(name);
                        // do some stats
                        ...
                    }
                }
                if (resolve) {
                    resolveClass(c);
                }
                return c;
            }
        }
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            throw new ClassNotFoundException(name);
        }
        ...
    }

    怎么自定义一个类加载器?

    加载一个类时,一般是调用类加载器的loadClass()方法来加载一个类,loadClass()方法的工作流程如下:

    1.先调用findLoadedClass(className)来获取这个类,判断类是否已加载。

    2.如果未加载,如果父类加载器不为空,调用父类加载器的loadClass()来加载这个类,父类加载器为空,就调用父类加载器加载这个类。

    3.父类加载器加载失败,那么调用该类加载器findClass(className)方法来加载这个类。

    所以我们我们一般自定义类加载器都是继承ClassLoader,来重新findClass()方法,来实现类加载。

    public class DelegationClassLoader extends ClassLoader {
      private String classpath;
    
      public DelegationClassLoader(String classpath, ClassLoader parent) {
        super(parent);
        this.classpath = classpath;
      }
    
      @Override
      protected Class<?> findClass(String name) throws ClassNotFoundException {
        InputStream is = null;
        try {
          String classFilePath = this.classpath + name.replace(".", "/") + ".class";
          is = new FileInputStream(classFilePath);
          byte[] buf = new byte[is.available()];
          is.read(buf);
          return defineClass(name, buf, 0, buf.length);
        } catch (IOException e) {
          throw new ClassNotFoundException(name);
        } finally {
          if (is != null) {
            try {
              is.close();
            } catch (IOException e) {
              throw new IOError(e);
            }
          }
        }
      }
    
      public static void main(String[] args)
          throws ClassNotFoundException, IllegalAccessException, InstantiationException,
          MalformedURLException {
        sun.applet.Main main1 = new sun.applet.Main();
    
        DelegationClassLoader cl = new DelegationClassLoader("java-study/target/classes/",
            getSystemClassLoader());
        String name = "sun.applet.Main";
        Class<?> clz = cl.loadClass(name);
        Object main2 = clz.newInstance();
    
        System.out.println("main1 class: " + main1.getClass());
        System.out.println("main2 class: " + main2.getClass());
        System.out.println("main1 classloader: " + main1.getClass().getClassLoader());
        System.out.println("main2 classloader: " + main2.getClass().getClassLoader());
        ClassLoader itrCl = cl;
        while (itrCl != null) {
          System.out.println(itrCl);
          itrCl = itrCl.getParent();
        }
      }
    }

    类加载的过程是什么样的?

    类加载器

    类加载器是 Java 运行时环境(Java Runtime Environment)的一部分,负责动态加载 Java 类到 Java 虚拟机的内存空间中。类通常是按需加载,即第一次使用该类时才加载。 由于有了类加载器,Java 运行时系统不需要知道文件与文件系统。每个 Java 类必须由某个类加载器装入到内存。

     

    类装载器除了要定位和导入二进制 class 文件外,还必须负责验证被导入类的正确性,为变量分配初始化内存,以及帮助解析符号引用。这些动作必须严格按一下顺序完成:

    1. 装载:查找并装载类型的二进制数据。
    2. 链接:执行验证、准备以及解析(可选) - -验证:确保被导入类型的正确性 -准备:为类变量分配内存,并将其初始化为默认值。
    • 解析:把类型中的符号引用转换为直接引用。
    1. 初始化:把类变量初始化为正确的初始值。

    装载

    类加载器分类

    在Java虚拟机中存在多个类装载器,Java应用程序可以使用两种类装载器:

    • Bootstrap ClassLoader:此装载器是 Java 虚拟机实现的一部分。由原生代码(如C语言)编写,不继承自 java.lang.ClassLoader 。负责加载核心 Java 库,启动类装载器通常使用某种默认的方式从本地磁盘中加载类,包括 Java API。
    • Extention Classloader:用来在<JAVA_HOME>/jre/lib/ext ,或 java.ext.dirs 中指明的目录中加载 Java 的扩展库。 Java 虚拟机的实现会提供一个扩展库目录。
    • Application Classloader:根据 Java应用程序的类路径( java.class.path 或 CLASSPATH 环境变量)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader() 来获取它。
    • 自定义类加载器:可以通过继承 java.lang.ClassLoader 类的方式实现自己的类加载器,以满足一些特殊的需求而不需要完全了解 Java 虚拟机的类加载的细节。

    全盘负责双亲委托机制

    在一个 JVM 系统中,至少有 3 种类加载器,那么这些类加载器如何配合工作?在 JVM 种类加载器通过 全盘负责双亲委托机制 来协调类加载器。

    • 全盘负责:指当一个 ClassLoader 装载一个类的时,除非显式地使用另一个 ClassLoader ,该类所依赖及引用的类也由这个 ClassLoader 载入。
    • 双亲委托机制:指先委托父装载器寻找目标类,只有在找不到的情况下才从自己的类路径中查找并装载目标类。

    全盘负责双亲委托机制只是 Java 推荐的机制,并不是强制的机制。实现自己的类加载器时,如果想保持双亲委派模型,就应该重写 findClass(name) 方法;如果想破坏双亲委派模型,可以重写 loadClass(name) 方法。

    装载入口

    所有Java虚拟机实现必须在每个类或接口首次主动使用时初始化。以下六种情况符合主动使用的要求:

    • 当创建某个类的新实例时(new、反射、克隆、序列化)
    • 调用某个类的静态方法
    • 使用某个类或接口的静态字段,或对该字段赋值(用final修饰的静态字段除外,它被初始化为一个编译时常量表达式)
    • 当调用Java API的某些反射方法时。
    • 初始化某个类的子类时。
    • 当虚拟机启动时被标明为启动类的类。

    除以上六种情况,所有其他使用Java类型的方式都是被动的,它们不会导致Java类型的初始化。 当类是被动引用时,不会触发初始化:

    1.通过子类去调用父类的静态变量,不会触发子类的初始化,只会触发包含这个静态变量的类初始化,例如执行这样的代码SubClass.fatherStaticValue只会触发FatherClass的初始化,不会触发SubClass的初始化,因为fatherStaticValue是FatherClass的变量

    2.通过数组定义类引用类,SuperClass[] array = new SuperClass[10];

    不会触发SuperClass类的初始化,但是执行字节码指令newarray会触发另外一个类[Lorg.fenixsoft.classloading.SuperClass的初始化,这个类继承于Object类,是一个包装类,里面包含了访问数组的所有方法,

    3.只引用类的常量不会触发初始化,因为常量在编译阶段进入常量池

    class SuperClass {
    		public static final String str = "hello";
    }
    
    //引用常量编译时会直接存入常量池
    System.out.println(SuperClass.str);
    

    对于接口来说,只有在某个接口声明的非常量字段被使用时,该接口才会初始化,而不会因为事先这个接口的子接口或类要初始化而被初始化。

    父类需要在子类初始化之前被初始化。当实现了接口的类被初始化的时候,不需要初始化父接口。然而,当实现了父接口的子类(或者是扩展了父接口的子接口)被装载时,父接口也要被装载。(只是被装载,没有初始化)

    验证

    确认装载后的类型符合Java语言的语义,并且不会危及虚拟机的完整性。

    • 装载时验证:检查二进制数据以确保数据全部是预期格式、确保除 Object 之外的每个类都有父类、确保该类的所有父类都已经被装载。
    • 正式验证阶段:检查 final 类不能有子类、确保 final 方法不被覆盖、确保在类型和超类型之间没有不兼容的方法声明(比如拥有两个名字相同的方法,参数在数量、顺序、类型上都相同,但返回类型不同)。
    • 符号引用的验证:当虚拟机搜寻一个被符号引用的元素(类型、字段或方法)时,必须首先确认该元素存在。如果虚拟机发现元素存在,则必须进一步检查引用类型有访问该元素的权限。

    准备

    在准备阶段,Java虚拟机为类变量分配内存,设置默认初始值。但在到到初始化阶段之前,类变量都没有被初始化为真正的初始值。

    类型默认值
    int0
    long0L
    short(short)0
    char’\u0000’
    byte(byte)0
    blooeanfalse
    float0.0f
    double0.0d
    referencenull

    解析的过程就是在类型的常量池总寻找类、接口、字段和方法的符号引用,把这些符号引用替换为直接引用的过程

    • 类或接口的解析:判断所要转化成的直接引用是数组类型,还是普通的对象类型的引用,从而进行不同的解析。
    • 字段解析:对字段进行解析时,会先在本类中查找是否包含有简单名称和字段描述符都与目标相匹配的字段,如果有,则查找结束;如果没有,则会按照继承关系从上往下递归搜索该类所实现的各个接口和它们的父接口,还没有,则按照继承关系从上往下递归搜索其父类,直至查找结束,

    初始化

    所有的类变量(即静态量)初始化语句和类型的静态初始化器都被Java编译器收集在一起,放到一个特殊的方法中,这个步骤就是初始化类静态变量和执行静态代码块。 对于类来说,这个方法被称作类初始化方法;对于接口来说,它被称为接口初始化方法。在类和接口的 class 文件中,这个方法被称为<clinit>

    1. 如果存在直接父类,且直接父类没有被初始化,先初始化直接父类。
    2. 如果类存在一个类初始化方法,执行此方法。

    这个步骤是递归执行的,即第一个初始化的类一定是Object

    Java虚拟机必须确保初始化过程被正确地同步。 如果多个线程需要初始化一个类,仅仅允许一个线程来进行初始化,其他线程需等待。

    这个特性可以用来写单例模式。

    Clinit 方法

    • 对于静态变量和静态初始化语句来说:执行的顺序和它们在类或接口中出现的顺序有关。
    • 并非所有的类都需要在它们的class文件中拥有()方法, 如果类没有声明任何类变量,也没有静态初始化语句,那么它就不会有<clinit>()方法。如果类声明了类变量,但没有明确的使用类变量初始化语句或者静态代码块来初始化它们,也不会有<clinit>()方法。如果类仅包含静态final常量的类变量初始化语句,而且这些类变量初始化语句采用编译时常量表达式,类也不会有<clinit>()方法。只有那些需要执行Java代码来赋值的类才会有()
    • final常量:Java虚拟机在使用它们的任何类的常量池或字节码中直接存放的是它们表示的常量值。

    JVM调优有哪些工具?

    jstat

    jstat可以打印出当前JVM运行的各种状态信息,例如新生代内存使用情况,老年代内存使用情况,Minor GC发生总次数,总耗时,Full GC发生总次数,总耗时。

    //5828是java进程id,1000是打印间隔,每1000毫秒打印一次,100是总共打印100次
    jstat -gc 5828 1000 100
    

    打印结果如下:

    各个参数的含义如下:

    S0C 新生代中第一个survivor(幸存区)的总容量 (字节)

    S1C 新生代中第二个survivor(幸存区)的总容量 (字节)

    S0U 新生代中第一个survivor(幸存区)目前已使用空间 (字节)

    S1U 新生代中第二个survivor(幸存区)目前已使用空间 (字节)

    EC 新生代中Eden(伊甸园)的总容量 (字节)

    EU 新生代中Eden(伊甸园)目前已使用空间 (字节)

    OC 老年代的总容量 (字节)

    OU 老年代代目前已使用空间 (字节)

    YGC 目前新生代垃圾回收总次数

    YGCT 目前新生代垃圾回收总消耗时间

    FGC 目前full gc次数总次数

    FGCT 目前full gc次数总耗时,单位是秒

    GCT 垃圾回收总耗时

    一般还可以使用jstat -gcutil <pid>:统计gc信息,这样打印出来的结果是百分比,而不是实际使用的空间,例如jstat -gcutil 1 1000 100

    例如,S0代表 新生代中第一个survivor区的空间使用了73.19%,E代表新生代Eden区使用了51%,O代表老年代食堂了98%

     

    参数描述
    S0年轻代中第一个survivor(幸存区)已使用的占当前容量百分比
    s1年轻代中第二个survivor(幸存区)已使用的占当前容量百分比
    E年轻代中Eden已使用的占当前容量百分比
    Oold代已使用的占当前容量百分比
    M元空间(MetaspaceSize)已使用的占当前容量百分比
    CCS压缩使用比例
    YGC年轻代垃圾回收次数
    FGC老年代垃圾回收次数
    FGCT老年代垃圾回收消耗时间
    GCT垃圾回收消耗总时间

     

    jstack

    jstack可以生成当前JVM的线程快照,也就是当前每个线程当前的状态及正在执行的方法,锁相关的信息。jstack -l 进程id ,-l代表除了堆栈信息外,还会打印锁的附加信息。jstack还会检测出死锁信息。一般可以用于定位线程长时间停顿,线程间死锁等问题。

    例如在下面的例子中,第一个线程获取到lock1,再去获取lock2,第二个线程先获取到lock2,然后再去获取lock1。每个线程都只获得了一个锁,同时在获取另外一个锁,就会进入死锁状态。

    public static void main(String[] args) {
            final Integer lock1 = new Integer(1);
            final String  lock2 = new String();
            ExecutorService executorService = Executors.newCachedThreadPool();
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    synchronized (lock1) {
                        System.out.println("线程1获得了lock1");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("线程1休眠结束");
                        System.out.println("线程1开始尝试获取lock2");
                        synchronized (lock2) {
                            System.out.println("线程1获得了lock2");
                        }
                    }
                }
            });
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    synchronized (lock2) {
                        System.out.println("线程2获得了lock2");
                        System.out.println("线程2开始尝试获取lock1");
                        synchronized (lock1) {
                            System.out.println("线程2获得了lock2");
                        }
                    }
                }
            });
        }

    使用jstack -l 进程id就可以打印出当前的线程信息

     

    以及各个线程的状态,执行的方法(pool-1-thread-1和pool-1-thread-2分别代表线程池的第一个线程和第二个线程):

     

    jmap

    一般可以生成当前堆栈快照。使用 jmap -heap可以打印出当前各个分区的内存使用情况,使用jmap -dump:format=b,file=dump.hprof 进程id可以生成当前的堆栈快照,堆快照和对象统计信息,对生成的堆快照进行分析,可以分析堆中对象所占用内存的情况,检查大对象等。执行jvisualvm命令打开使用Java自带的工具Java VisualVM来打开堆栈快照文件,进行分析。可以用于排查内存溢出,内存泄露问题。

    也可以配置启动时的JVM参数,让发送内存溢出时,自动生成堆栈快照文件。

    //出现 OOM 时生成堆 dump: 
    -XX:+HeapDumpOnOutOfMemoryError
    //生成堆文件地址:
    -XX:HeapDumpPath=/home/liuke/jvmlogs/
    

    查看内存使用情况

     

    jmap -histo打印出当前堆中的对象统计信息,包括类名,每个类的实例数量,总占用内存大小。

    instances列:表示当前类有多少个实例。
    bytes列:说明当前类的实例总共占用了多少个字节
    class name列:表示的就是当前类的名称,class name 对于基本数据类型,使用的是缩写。解读:B代表byte ,C代表char ,D代表double, F代表float,I代表int,J代表long,Z代表boolean 
    前边有[代表数组,[I 就相当于int[] 
    对象数组用`[L+类名`表示 

     

    使用jmap -dump:format=b,file=/存放路径/heapdump.hprof 进程id就可以得到堆转储文件,然后执行jvisualvm命令就可以打开JDK自带的jvisualvm软件。

    例如在这个例子中会造成OOM问题,通过生成heapdump.hprof文件,可以使用jvisualvm查看造成OOM问题的具体代码位置。

    public class Test018 {
    
        ArrayList<TestObject> arrayList = new ArrayList<TestObject>();
    
        public static void main(String[] args) {
            Test018 test018 =new Test018();
            Random random = new Random();
            for (int i = 0; i < 10000000; i++) {
                TestObject testObject = new TestObject();
                test018.arrayList.add(testObject);
            }
        }
        private static class TestObject {
            public byte[] placeholder = new byte[64 * 1024];//每个变量是64k
        }
    }
    
    -Xms20m -Xmx20m -verbose:gc -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/存放路径/heapdump.hprof
    

    造成OOM问题的代码位置:

     

    然后在主页点击Histogram,进入Histogram页面可以看到对象列表,with incomming references 也就是可以查看所有对这个对象的引用(思路一般优先看占用内存最大对象;其次看数量最多的对象。)。我们这个例子中主要是byte[]数组分配了占用了大量的内存空间,而byte[]主要来自于Test018类的静态变量arrayList的每个TestObject类型的元素的placeholder属性。

     

    同时可以点击 内存快照对比 功能对两个dump文件进行对比,判断两个dump文件生成间隔期间,各个对象的数量变化,以此来判断内存泄露问题。 

     

    展开全文
  • 国家统计局2019年行政区域划分全代码,处理成台账形式,便于使用。涵盖省、市、县(区)、镇(街道)、乡级别。
  • 初识JVM内存区域划分

    千次阅读 2020-12-31 20:24:26
    初识JVM内存区域划分初识JVM内存区域划分总结: 初识JVM内存区域划分 我们只带,当Java源代码(.java)文件被编译后,会变成字节码(.class)文件,它是一个二进制文件。然后字节码文件就会被加载进Java虚拟机...
  • Java内存区域划分

    2017-03-15 14:03:30
    JVM在执行Java程序时会讲它所管理的内存划分为若干个数据区域。目前比较流行的划分方式是将内存区域划分为堆(Heap)内存和栈(Stack)内存。这种方法的划分说明与对象分配关系最为密切的内存是很多程序员最关注的部分,...
  • JVM内存区域划分

    千次阅读 2018-11-29 11:06:20
    Java程序是交由JVM执行的,所以我们在谈Java内存区域划分的时候事实上是指JVM内存区域划分。在讨论JVM内存区域划分之前,先来看一下Java程序具体执行的过程:   首先Java源代码文件(.java后缀)会被Java编译器编译...
  • 中国区域划分

    万次阅读 2018-09-08 09:28:38
    国家规定的中国区域划分方法如下: 1.华北地区:北京、天津、河北、山西、内蒙古(5个省、区、市) 2.东北地区:辽宁、吉林、黑龙江、大连(4个省、市) 3.华东地区:上海、江苏、浙江、安徽、福建、江西、山东、...
  • 类型被分为两种: 两者在内存中的存储方式 值类型:只需要一段单独的内存,用于存储实际的数据,(单独定义的时候放在栈中) 引用类型:需要两段内存 第一段存储实际的数据,它总是位于堆中 第二段是一个引用,指向...
  • 探讨了区域土地类型的划分依据及生态特点,并结合区域地理条件,将该区土地类型划分为三级;并对该区的土地类型结构、演替和生态设计作了系统分析;从而为合理开发利用该区土地资源,提供了理论依据.
  • JAVA内存区域划分

    千次阅读 2018-07-13 21:58:58
    大家好,今天和大家分享java内存区域划分知识。 通常我们把java的内存区域粗略划分为栈内存和堆内存,但是这只能说明程序员平常比较关心的是这两块内存,其实JAVA的内存划分却不止这两块内存,而要更加复杂一些。 ...
  • OSPF协议将其管理的网络划分为不同类型的若干区域(Area),其中标准区域特点是(64);存根区域(stub)的特点是(65)。 (64)A.不接受本地AS之外的路由信息,也不接受其他区域的路由汇总信息 B.不接受本地AS...
  • JVM的内存区域划分

    2018-06-14 15:56:37
    JVM的内存区域划分学过C语言的朋友都知道C编译器在划分内存区域的时候经常将管理的区域划分为数据段和代码段,数据段包括堆、栈以及静态数据区。那么在Java语言当中,内存又是如何划分的呢?由于Java程序是交由JVM...
  • 内存中的区域划分

    2020-02-27 13:03:51
    本篇博客主要介绍在C程序中的内存区域划分,内存被分为一些不同的区域,各有不同的功能和特性,下面是区域的分类: C语言中的区域划分: 栈区stack 堆区heap 静态区static 常量区 C++中的区域划分: 栈区stack ...
  • JVM内存区域划分

    2018-05-18 13:53:14
    1.概述 JVM内存区域简单的可以划分为java堆和java栈,详细的可以划分为方法区,堆,程序计数器,虚拟机栈和本地方法栈。 其中方法区和堆是所有线程共享的,本地方法栈,虚拟机栈和程序计数器是线程独占的。2.详细...
  • 序号 风险点名称 类型 可能导致的主要事故类型 区域位置 所属单位 备注 1 外来原料货车上下地磅 作业活动 高处坠落、车辆伤害、物体打击、其他伤害、噪声 原料区 原料岗位 2 外来原料货车厂区运行 作业活动 高处...
  • 作为宏观管理区域社会经济持续发展的一种新模式,生态经济分区,在协调区域经济发展与生态环境保护以及合理利用自然资源的关系上,从生态经济学原理出发,选择了37个适合山东省生态经济系统分类的特征指标,运用...
  •  内存中对数据的存储不是杂乱无章的,而是有相应的划分,根据数据类型分门别类安放到相应的位置。  存储的区域由最高存储地址到最低存储地址依次为: 命令行参数区:命令行参数和环境变量;栈区(stack):...
  • OSPF的区域划分与路由计算概述

    千次阅读 2020-03-30 09:42:42
    OSPF的区域划分与路由计算 ...2.2 OSPF的区域类型 骨干区域:Area0区域即为骨干区域,其他区域必须连接在骨干区域,同样其他区域也通过骨干区域交换信息 传输区域:需要将路由信息转发给其他区域 末端区...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 144,342
精华内容 57,736
关键字:

区域类型的划分