啰里吧嗦jvm
一.为什么要了解jvm
有次做项目的时候,程序run起来的时候,总是报OutOfMemoryError,有老司机教我们用jconsole.exe看内存溢出问题
就是这货
启动jconsole后,发现一个是进程是它自己,一个是我的eclipse,哈哈,现在还不习惯idea, 还一个就是我们项目启动的进程了,项目我用的maven搭建的,引入了tomcat插件,
jconsole可以看很多东西, 需要大家慢慢摸索,包括可以检测死锁,当时的项目启动程序,就看堆内存占用的空间不断上升,最后报错,
当时银行的jvm内存设置已经比较大了,最后发现启动时日志打印不合理,打印的东西太多,占用内存空间太大
了解jvm可以让我们排查问题多出一个角度,而不是每次都是重启解决问题,当然一般情况下都是逻辑代码写的不好,产生了大量的内存,导致堆不够用发生
- jvm内存分为 堆 和 非堆
- 堆又分为:
- 1.老年代
- Old Gen:年轻代的对象如果能够挺过数次收集,就会进入老人区
- 2.新生代
- Eden Space(伊甸园):一个Eden,所有新建对象都会存在于该区
- Survivor Space(幸存者区):两个Survivor区,用来实施复制算法
- 3.持久代
- permanent:装载Class信息等基础数据,默认64M,一般情况下,持久代是不会进行GC的
- -XX:MaxPermSize=
- -Xms 是堆的初始内存
- -Xmx 是堆的最大内存
- -Xmn 年轻代大小
- -Xss 每个线程的堆栈大小
- 通过java -X 命令可查看
- java –Xms128m –Xmx128m
非堆分为
Metaspace
Code Cache
Compressed Class Space
二.当你输入java Hello时发生了什么
现在我们当然使用了各种集成的开发工具,比如eclipse,
最原始的执行java程序,
首先你得先按照好jdk,然后配置好环境变量, 让系统能在任意目录下都能找到java命令 并执行
其次写一个java文件,符合规范,比如类名和文件名一致,类要是public的,javac会对其进行校验
通过cmd打开windows的命令窗口, 使用javac xxx.java 命令将其编译成字节码文件, java虚拟机只认字节码文件,无论你是通过什么途径得到它的,只要符合字节码文件规范,就能在不同系统上的jvm运行,我们开发通常在windows环境, 然后部署项目在linux环境,只要一次编译,到处运行
执行java xxx 命令,这里jvm究竟做了什么呢
- String sourcePath = "C:/Users/"+System.getProperties().getProperty("user.name")+"/Desktop";
- JavaFile javaFile = JavaFile.builder("proxy", typeSpecBuilder.build()).build();
- // 为了看的更清楚,我将源码文件生成到桌面
- javaFile.writeTo(new File(sourcePath));
- // 编译
- JavaCompiler.compile(new File(sourcePath + "/proxy/Proxy.java"));
- // 使用反射load到内存
- URLClassLoader classLoader = new URLClassLoader(new URL[] { new URL("file:C:\\Users\\"+System.getProperties().getProperty("user.name")+"\\Desktop\\") });
- Object obj = null;
- //Classloader:类加载器,你可以使用自定义的类加载器,我们的实现版本为了简化,直接在代码中写死了Classloader。
- Class clazz1 = classLoader.loadClass("proxy.Proxy");
- System.out.println(clazz1);
- System.out.println(clazz1.getDeclaredConstructors().getClass());
- //将生成的TimeProxy编译成class 使用类加载器加载进内存中 再通过反射或者该类的构造器
- //再通过构造器将其代理类 TimeProxy 构造出来
- //NoSuchException 打印classz信息 发现 刚开始创建类 没有使用public
- Constructor constructor = clazz1.getConstructor(InvocationHandler.class);
- System.out.println("constructor" + constructor);
- obj = constructor.newInstance(handler);
报错那就是少引了一个jar包
<dependency>
<groupId>com.squareup</groupId>
<artifactId>javapoet</artifactId>
<version>1.8.0</version>
</dependency>
首先根据内存配置,为jvm申请内存空间,并按照 jvm的 【内存模型】 划分好 区域
创建一个引导类加载器实例,初步加载系统类到内存方法区区域中;
创建JVM 启动器实例Launcher
并取得类加载器ClassLoader
使用上述获取的ClassLoader实例加载我们定义的类
加载完成时候JVM会执行Main类的main方法入口,执行Main类的main方法结束,java程序运行结束,JVM销毁
上面的看看就行了,说的很笼统,基本流程知道就ok,就是jvm里面也是各种代码实现的,有用到c++,下图是看程序jvm内存大小
我的电脑 默认是128M 和 1.75G
三.jvm内存模型
3.1 运行时区域
xxx.java文件通过 JavaCompiler java编译器 编译成xxx.class文件----
执行java命令其实就启动了java.exe,启动一个Java程序时,一个JVM实例就产生了
JVM实例对应了一个独立运行的java程序它是进程级别------
jvm实例创建启动器获得类加载器
classLoader,加载.class文件进内存
由谁来执行,由执行引擎来执行, 执行引擎可以通过解释器 或者 即时编译器 去执行指令
反汇编命令
javap -c xxx Java 字节码的指令
在JVM实现中,线程为Execution Engine的一个实例,main函数是JVM指令执行的起点
JVM会创建main线程来执行main函数,以触发JVM一系列指令的执行
classLoader,加载.class文件进内存可以细分为3步骤
3.1.1 加载 xxx.class是一个二进制字节码文件, 加载到method are方法区
3.1.2 链接 可以分为3步
3.1.2.1 验证 验证.class文件在结构上满足JVM规范的要求,验证工作可能会引起其他类的加载但不进行验证和准备
3.1.2.2 准备 正式为类变量分配内存并设置类变量初始值的阶段,,“通常情况”下是数据类型的零值,特殊情况就是你定义了是static final ,那么jvm会识别为常量,为该变量设置一个ConstantValue属性,准备阶段就初始化
3.1.2.3 解析 解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程
3.1.2.4 初始化 简单来说真正开始赋值, 收集类变量 , 静态块, 执行类构造器,虚拟机也规定了5种情况需要对类做个初始化, 例如初始化子类发现父类还没初始化过, 那么先初始化父类, 每个类在初始化前 ,前面的加载链接都是必须执行的,每个类只被初始化一次
- 初始化还有很多规则,比如使用数组定义来引用类, 不会触发该类的初始化, DemoTest[] a = new DemoTest[10],假如DemoTest里有类变量,静态块,则不会初始化
- public class ClassLoadTest {
- public static void main(String[] args) {
- SuperCla[] a = new SuperCla[11];
- }
- }
- class SuperCla{
- static {
- System.out.println("xxxxx");
- }
- }
执行结果是不输出任何结果
例如子类引用定义在父类的静态变量,则不会触发子类的初始化
例如编译阶段就有常量属性的,就是那种final static ,就不会触发类的初始化, 因为直接从常量池中取
一个经典的例子
- public class ClassLoadTest {
- public static void main(String[] args) {
- }
- static {
- System.out.println("1");
- }
- static ClassLoadTest a = new ClassLoadTest();
- static {
- System.out.println("2");
- }
- {
- System.out.println("6");
- }
- ClassLoadTest(){
- System.out.println("3");
- System.out.println("b=" + b + ",c =" + c + ",d = " + d+ ",e=" + e);
- }
- int b = 100;
- static int c = 200;
- static final int d = 300;
- static int e = 400;
- static void func() {
- System.out.println("第二次c=" + c );
- System.out.println("4");
- }
- }
1
6
3
b=100,c =0,d = 300,e=0
2
所以归到该类上就是 java ClassLoadTest 命令
jvm 加载 ClassLoadTest 类
|
验证 没毛病
|
准备 类变量要分配内存 并设置初始值了 里面有3个类变量 static ClassLoadTest a = new ClassLoadTest();
static int c
static final int d
a呢 就初始值就是null c内初始值是0 ,因为这个通常情况下是数据类型的零值 意外情况就是加了final关键字 所以会为d生成一个ConstantValue属性,该属性值是300
|
本来准备完了是要继续初始化的 ,但是由于 类变量的初始化是它自身 实例的初始化 有new 关键字
所以 就先初始化 该类的实例了,
先执行非静态块 ,再执行构造方法
类的初始化就直接中断掉了....
所以先打印出来的 c 是0
(如果你在func里 打印c的值 会发现c还是200)
|
当前面的加载、链接、初始化都结束后,JVM 会调用 ClassLoadTest 的 main 方法。被调用的 main 必须被修饰为 public , static, void
|
按照源码的顺序 执行静态代码块 和 类变量 ---> 先打印1 --->然后就跑偏了 静态变量new了自己的一个实例 于是去初始化自己的实例--->
先执行 非静态块 打印6-----> 再执行构造方法---->打印3---->打印bcd变量的值 此时 b是实例变量,成语变量 new ClassLoadTest()进行初始化,所以是100
c 是类变量,由于前面准备阶段---先去执行了 ClassLoadTest a = new ClassLoadTest(); c还是零值 ,d由于是final static, 准备阶段就默认给个值
a= null b=100 c=0 d=300---------------
在按照源码顺序执行 第二个静态代码块 打印--2
具体的类加载流程我们知道了, 现在来看看类加载到jvm内存后, 把 哪些东西 放在内存模型的 哪个位置
首先来看线程共享的区域: 方法区 和 堆
1.先看方法区 method area , 存储了每个类的信息(包括类的名称、方法信息、字段信息)、静态变量、常量、(即时)编译器编译后的代码,
Class文件中还有常量池,存储编译期间生成的字面量和符号引用,
而方法区里面的【运行时常量池】就是该类或接口的运行时表示
在这里要介绍下String 的 intern方法, 该方法在运行期间也可将新的常量 放进常量池, 大大减小堆的空间
new String(“abc”).intern()
abc 会出现在堆里是一个新的对象, 调用intern方法后, 会把abc 放在常量池中
并返回指向该常量的引用
- intern用来返回常量池中的某字符串,如果常量池中已经存在该字符串,则直接返回常量池中该对象的引用。
- 否则,在常量池中加入该对象,然后 返回引用。
- 原文举例:
- Now lets understand how Java handles these strings. When you create two string literals:
- String name1 = "Ram";
- String name2 = "Ram";
- In this case, JVM searches String constant pool for value "Ram", and if it does not find it there
- then it allocates a new memory space and store value "Ram" and return its reference to name1.
- Similarly, for name2 it checks String constant pool for value "Ram" but this time it find "Ram"
- there so it does nothing simply return the reference to name2 variable.
- The way how java handles only one copy of distinct string is called String interning.
- 举例
- String s1 = new String("aaa").intern();
- String s2 = "aaa";
- System.out.println(s1 == s2); // true
2.堆: 存储对象及数组本身, 而非引用,是垃圾收集器管理的主要区域
3.接下来就是每个线程独有的了,本地方法栈,jvm栈, 在HotSpot中,没有JVM Stacks和Native Method Stacks之分,功能上已经合并,所以放在一起介绍,原理都类似
JVM 栈中存放的是一个个的栈帧,每个栈帧对应一个被调用的方法,所以很容易想到局部变量肯定在栈帧中,因为方法里定义的是局部变量,还有些其他的看图
- 栈的使用是比如 线程执行test方法,就会创建test的栈帧,并将此栈帧压栈,发现inner()方法,又创建一个栈帧,压栈, 等inner()
- 方法执行完毕,则将inner()出栈,在将test()出栈
- public class A {
- void test() {
- inner();
- }
- void inner(){
- }
4.最后一个 程序计数器
在JVM中,多线程是通过线程轮流切换来获得CPU的执行时间, 就是说在微观上,线程都是走走停停, 在某一个时刻一个cpu的内核只会执行 某一条线程的指令, 那么需要一个工具记录之前线程执行到哪儿了,只是打个比方 A线程执行到3这个指令的位置停住了, 把cpu让给线程B, 线程B执行完毕后, A线程怎么知道刚才执行到哪儿了呢
- 那么只能是用一个 每个线程独有的 程序计算器 来记住A线程---位置3 ,这个也很好理解这个占用内存大小是固定的
- public void test();
- Code:
- 0: new #17 // class java/lang/String
- 3: dup
- 4: ldc #19 // String 1
- 6: invokespecial #21 // Method java/lang/String."<init>":
- (Ljava/lang/String;)V
- 9: astore_1
- 10: aload_1
- 11: invokevirtual #24
四.GC垃圾回收
首先我们要确定GC垃圾回收主要作用的区域, 由于 jvm栈,本地方法栈,程序计数器都是随线程生灭的,所以gc主要是对方法区和堆里面的内容进行回收
而堆又是占内存最大的一块区域,存放对象实例,所以也是回收重点。
这些知识有点抽象,所以我们希望很直观的看到, 用 cmd 命令行执行class文件的时候 指定参数也可以看,不过我们现在都是用eclipse
选择xx.java --右击---Run as -> Run configurations
- java应用名 -arguments -VM arguments,加入jvm参数
- -Xms20m --jvm堆的最小值
- -Xmx20m --jvm堆的最大值
- -XX:+PrintGCTimeStamps -- 打印出GC的时间信息
- -XX:+PrintGCDetails --打印出GC的详细信息
- -verbose:gc --开启gc日志
- -Xloggc:d:/gc.log -- gc日志的存放位置
- -Xmn10M -- 新生代内存区域的大小
- -XX:SurvivorRatio=8 --新生代内存区域中Eden和Survivor的比例
确保设置的是自己要测试的类, 不是的话先执行一遍
那具体采取什么措施去回收这些对象呢,针对方法区的变量,类信息回收, 某些虚拟机的永久代, 条件比较苛刻, 而且占用空间小, 而且可以通过配置决定,在这里不过多讨论
回收的判定-对象死不死, 回收的时机--死了何时处理, 回收的方法策略--火化还是土葬
1.回收的判定: 针对堆的对象实例进行回收,先判断这些对象有没有用, 能不能被回收
有两种算法
- Java不使用的引用计数算法
- :当对象有个地方引用它,计数器+1, 引用失效计数器-1, 计数器为0则判定为不可使用对象
- 缺点:无法解决对象互相 循环引用
- public class Test {
- public Test test;
- public static void main(String[] args){
- Test t1 = new Test();
- Test t2 = new Test();
- t1.test = t2;
- t2.test = t1;
- System.gc();
- }
- }
2.可达性分析算法(根搜索算法):
这个算法的基本思想是通过一系列称为“GC Roots”的对象作为起始点,
从这些节点向下搜索,搜索所走过的路径称为引用链,
当一个对象到GC Roots没有任何引用链(即GC Roots到对象不可达)时,则证明此对象是不可用的。
在Java语言中,可以作为GCRoots的对象包括下面几种:
(1). 虚拟机栈(栈帧中的局部变量区,也叫做局部变量表)中引用的对象。
(2). 方法区中的类静态属性引用的对象。
(3). 方法区中常量引用的对象。
(4). 本地方法栈中JNI(Native方法)引用的对象。
8,9,10会被回收
2. 回收的时机:
即使是被判断不可达的对象,也要再进行筛选,当对象没有覆盖finalize()方法,
或者finalize方法已经被虚拟机调用过,则没有必要执行;
如果有必要执行——放置在F-Queue的队列中——Finalizer线程执行。
注意:对象可以在被GC时可以自我拯救(this),机会只有一次,因为任何一个对象的finalize()方法都只会被系统自动调用一次
。并不建议使用,应该避免。使用try_finaly或者其他方式。
这个只是判断哪些对象存活,哪些对象死亡,并不真正进行回收, 而且即使被判定为不可达对象, 回不回收还是要经过两次标记(了解)
1. 如果对象在进行可达性分析后发现没有与GCRoots相连的引用链,
(1)该对象被第一次标记并进行一次筛选
筛选条件为是否有必要执行该对象的finalize方法,若对象没有覆盖finalize方法或者该finalize方法是否已经被虚拟机执行过了
,则均视作不必要执行该对象的finalize方法,即该对象将会被回收。
反之,若对象覆盖了finalize方法并且该finalize方法并没有被执行过,那么,这个对象会被放置在一个叫F-Queue的队列中,
之后会由虚拟机自动建立的、优先级低的Finalizer线程去执行,而虚拟机不必要等待该线程执行结束,即虚拟机只负责建立线程,
其他的事情交给此线程去处理。
(2).对F-Queue中对象进行第二次标记,如果对象在finalize方法中拯救了自己,即关联上了GCRoots引用链,
如把this关键字赋值给其他变量,那么在第二次标记的时候该对象将从“即将回收”的集合中移除,如果对象还是没有拯救自己,
那就会被回收。如下代码演示了一个对象如何在finalize方法中拯救了自己,然而,它只能拯救自己一次,第二次就被回收了。
- package testGc;
- /*
- * 此代码演示了两点:
- * 1.对象可以再被GC时自我拯救
- * 2.这种自救的机会只有一次,因为一个对象的finalize()方法最多只会被系统自动调用一次
- * */
- public class FinalizeEscapeGC {
- public String name;
- public static FinalizeEscapeGC SAVE_HOOK = null;
- public FinalizeEscapeGC(String name) {
- this.name = name;
- }
- public void isAlive() {
- System.out.println("yes, i am still alive :)");
- }
- //如果判断为该对象不可达
- //第一次标记
- //对象没有覆盖finalize方法或者该finalize方法是否已经被虚拟机执行过了
- //则均视作不必要执行该对象的finalize方法,即该对象将会被回收
- //此FinalizeEscapeGC对象覆盖了finalize() 且该finalize方法并没有被执行过
- //这个对象会被放置在一个叫F-Queue的队列中
- //之后会由虚拟机自动建立的、优先级低的Finalizer线程去执行
- //第二次标记
- //关联上了GCRoots引用链则该对象将从“即将回收”的集合中移除
- //否则会被回收
- //只能第一次执行的时候可以拯救自己一次 不被回收
- @Override
- protected void finalize() throws Throwable {
- super.finalize();
- System.out.println("finalize method executed!");
- System.out.println(this);
- FinalizeEscapeGC.SAVE_HOOK = this;
- //关联上了GCRoots引用链
- //把this关键字赋值给其他变量
- //方法区中的类静态属性引用的对象
- }
- @Override
- public String toString() {
- return name;
- }
- public static void main(String[] args) throws InterruptedException {
- SAVE_HOOK = new FinalizeEscapeGC("leesf");
- System.out.println(SAVE_HOOK);
- // 对象第一次拯救自己
- SAVE_HOOK = null;
- System.out.println(SAVE_HOOK);
- // 两次标记 1.重写了finalize方法且没有执行过 放队列
- // 2.队列中坚持是不是有 关联上引用链 有即可以拯救自己一次
- System.gc();
- // 因为finalize方法优先级很低,所以暂停0.5秒以等待它
- Thread.sleep(500);
- if (SAVE_HOOK != null) {
- SAVE_HOOK.isAlive();
- } else {
- System.out.println("no, i am dead : (");
- }
- // 下面这段代码与上面的完全相同,但是这一次自救却失败了
- // 一个对象的finalize方法只会被调用一次
- SAVE_HOOK = null;
- System.gc();
- // 因为finalize方法优先级很低,所以暂停0.5秒以等待它
- Thread.sleep(500);
- if (SAVE_HOOK != null) {
- SAVE_HOOK.isAlive();
- } else {
- System.out.println("no, i am dead : (");
- }
- }
- }
重点就是finalize方法只会执行一次,只有第一次可以拯救自己
3.而是通过垃圾收集算法和垃圾收集器来具体执行回收,
垃圾收集器是 垃圾收集算法的具体实现
- 1、标记-清除(Mark-Sweep)算法
- 首先标记出所有需要回收的对象,标记完成后统一回收所有被标记的对象
- 效率低
- 2、复制(Copying)算法
- 它将可用的内存分为两块,每次只用其中一块,当这一块内存用完了
- 就将还存活着的对象复制到另外一块上面,然后再把已经使用过的内存空间一次性清理掉
- 分代收集算法就把内存划分为几块, 通常在新生代用复制算法
- 【新生代
- Eden Space(伊甸园):一个Eden,所有新建对象都会存在于该区
- Survivor Space(幸存者区):两个Survivor区,用来实施复制算法】
- 如果采取1:1, 那非常不划算,意味着可用内存只能用一半,通常新生代的内存 Eden + Survivor 占90%, Survivor空闲占10%,
- 回收就 先复制 eden和survivor活着的对象到 Survivor, 再清理调eden 和 survivor
- (HotSpot虚拟机默认Eden区和Survivor区的比例为8:1)
- 3、标记-整理(Mark-Compact)算法
- 分代收集算法就把内存划分为几块, 通常在老年代用标记整理法
- 因为老年代通常都是不易被回收的对象, 采用复制算法非常不划算, 存活90%, 复制后还是90%
- 让所有存活对象都向一端移动,然后直接清理掉边界以外的内存
- 4、分代收集算法
- 以上内容的结合, 根据对象的生命周期,内存划分 新生代, 老年代,
- 大批对象死去、少量对象存活的(新生代),使用复制算法
- 对象存活率高、没有额外空间进行分配担保的(老年代)采用其他两个方法</pre><figure><hr/></figure><h2>五.JVM性能调优</h2><pre class="prism-token token language-js">通过上面的一些理论, 具体的实践还是在项目中就具体情况进行分析, 当然99%的oom 都是代码问题导致的,
- 比如打印日志占用的内存空间不足,
- 比如某个线程产生大量对象缺没有被释放
- 少量是设置原因,比如最大堆内存设置的太小,
- 比如卡顿是因为频繁发生FUll GC, 那么如何调整老年代和新生代的大小
五.JVM性能调优
通过上面的一些理论, 具体的实践还是在项目中就具体情况进行分析, 当然99%的oom 都是代码问题导致的,\n比如打印日志占用的内存空间不足,\n比如某个线程产生大量对象缺没有被释放\n\n少量是设置原因,比如最大堆内存设置的太小,\n比如卡顿是因为频繁发生FUll GC, 那么如何调整老年代和新生代的大小
六.守护线程
任何非守护线程还在运行,程序就不会终止, 在所有用户线程都终止了, 那么守护线程也终止,虚拟机退出
最典型的应用就是GC(垃圾回收器)
啰里吧嗦jvm的更多相关文章
- 啰里吧嗦CountDownLatch
java.util.concurrent Class CountDownLatch 目录 CountDownLatch 是什么 CountDownLatch是一个同步工具类,它允许一个或多个线程一直等 ...
- 啰里吧嗦redis
1.redis是什么 redis官网地址 Redis is an open source (BSD licensed), in-memory data structure store, used as ...
- 啰里吧嗦kafka
1.kafka是什么 kafka官网: http://kafka.apache.org/ kafka是一种高吞吐量的分布式发布订阅消息系统,用它可以在不同系统中间传递分发消息 2.zookeeper是 ...
- 啰里吧嗦式讲解java静态代理动态代理模式
一.为啥写这个 文章写的比较啰嗦,有些东西可以不看,因为想看懂框架, 想了解SSH或者SSM框架的设计原理和设计思路, 又去重新看了一遍反射和注解, 然后看别人的博客说想要看懂框架得先看懂设计模式,于 ...
- 一步步学习操作系统(1)——参照ucos,在STM32上实现一个简单的多任务(“啰里啰嗦版”)
该篇为“啰里啰嗦版”,另有相应的“精简版”供参考 “不到长城非好汉:不做OS,枉为程序员” OS之于程序员,如同梵蒂冈之于天主教徒,那永远都是块神圣的领土.若今生不能亲历之,实乃憾事! 但是,圣域不是 ...
- 啰哩吧嗦式讲解在windows 家庭版安装docker
1.docker是什么,为什么要使用docker Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中, 然后发布到任何流行的 Linux 机器上,也可以实 ...
- IDDD 实现领域驱动设计-理解领域和子域
上一篇:<IDDD 实现领域驱动设计-一个简单业务用例的回顾和理解> 在<实现领域驱动设计>第二章的前半部分内容中,提到领域和子域的概念,并且作者把这两者又进行了细致的区分,其 ...
- Css 动画的回调
在做项目中经常会遇到使用动画的情况.以前的情况是用js写动画,利用setTimeout函数或者window.requestAnimationFrame()实现目标元素的动画效果.虽然后者解决了刷新频率 ...
- 浅谈独立使用NDK编译库文件(Android)
阅读前准备 这是一篇相对入门的文章.文中会涉及到少许NDK的知识,但个人认为对初学者来说都相对比较实用,因为都是在平时项目中遇到的(目前自己也是初学者).一些其他高深的技术不再本文探讨范围之内(因为我 ...
随机推荐
- http 协议 c++代码 获取网页
最近接触了些 html 和 JavaScript,索性了解下 http 协议.在园子里找到了 HTTP协议详解,图文并茂,很爽! 于是小小的尝试了下 #include <WinSock2.h&g ...
- 使用python进行短信轰炸
本文作者:i春秋作家——Hacker1ee 大家好,我是1ee(因为在作家群,就不加Hacker这个前缀了,怕被大佬打..) 刚加入i春秋作家组希望大家多多关照,也欢迎大家找我交流 今天我来讲讲我最近 ...
- grafana 运行
1,下载好项目,然后进入到目录 键入: ./bin/grafana-server web 运行 https://www.waitig.com/grafana-config-and-run.html 2 ...
- php curl 伪造IP来源的实例代码
来源:http://www.jb51.net/article/31694.htm curl 它不但可以模仿用户登录,还可以模仿用户IP地址哦,为伪造IP来源,本实例仅供参考哦 //伪造ip ; $i ...
- JPA总结——实体关系映射(一对多@OneToMany)
JPA总结——实体关系映射(一对多@OneToMany) 注意:本文出自“阿飞”的博客,如果要转载本文章,请与作者联系! 并注明来源: http://blog.sina.com.cn/s/blog_4 ...
- HTML简单登录和注册页面及input标签诠释
今天第一次接触HTML这种语言,虽然不能完全理解其中的意思,过去学的英语单词几乎也忘了差不多了,但是感觉进入这门语言学习之后就没有那么难了,一步一步来吧!下面巩固下今天学内容: HTML是一种超文本标 ...
- NOIP上机测试注意事项
由于近期模拟题原地**次数较多,故写一篇警示文 1,头文件 1.1正式比赛中,反正我不敢用bits/stdc++.h. 1.2正式比赛中,建议打出以下十个库 #include<iostream& ...
- apache2.4配置weblogic12c集群(linux环境)
首先确定环境已装apache2.4,没装的话可以看下这篇文章apache2.4一键脚本安装(linux环境) 1.下载apache分发模块mod_wl_24.so 下载apache2.4的weblog ...
- Linux驱动:LCD驱动测试
(1) 进入内核源码目录中,make menuconfig -> Device Drivers -> Graphics support -> [M]Support for frame ...
- 再学Java 之 形参个数可变函数
自Java 5后,Java允许定义形参个数可变的方法,从而允许运行过程中,为方法指定不确定个数的形参. 其定义方法的格式如下: void function_name ( type ... variab ...