精华内容
下载资源
问答
  • 全面理解Java内存模型

    万次阅读 多人点赞 2016-09-21 18:39:21
    Java内存模型即Java Memory Model,简称JMM。JMM定义了Java 虚拟机(JVM)在计算机内存(RAM)中的工作方式。JVM是整个计算机虚拟模型,所以JMM是隶属于JVM的。如果我们要想深入了解Java并发编程,就要先理解好Java内存...

    Java内存模型即Java Memory Model,简称JMM。JMM定义了Java 虚拟机(JVM)在计算机内存(RAM)中的工作方式。JVM是整个计算机虚拟模型,所以JMM是隶属于JVM的。

    如果我们要想深入了解Java并发编程,就要先理解好Java内存模型。Java内存模型定义了多线程之间共享变量的可见性以及如何在需要的时候对共享变量进行同步。原始的Java内存模型效率并不是很理想,因此Java1.5版本对其进行了重构,现在的Java8仍沿用了Java1.5的版本。

    关于并发编程

    在并发编程领域,有两个关键问题:线程之间的通信同步

    线程之间的通信

    线程的通信是指线程之间以何种机制来交换信息。在命令式编程中,线程之间的通信机制有两种共享内存消息传递

    共享内存的并发模型里,线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态来隐式进行通信,典型的共享内存通信方式就是通过共享对象进行通信。

    消息传递的并发模型里,线程之间没有公共状态,线程之间必须通过明确的发送消息来显式进行通信,在java中典型的消息传递方式就是wait()notify()

    关于Java线程之间的通信,可以参考线程之间的通信(thread signal)

    线程之间的同步

    同步是指程序用于控制不同线程之间操作发生相对顺序的机制。

    在共享内存并发模型里,同步是显式进行的。程序员必须显式指定某个方法或某段代码需要在线程之间互斥执行。

    在消息传递的并发模型里,由于消息的发送必须在消息的接收之前,因此同步是隐式进行的。

    Java的并发采用的是共享内存模型

    Java线程之间的通信总是隐式进行,整个通信过程对程序员完全透明。如果编写多线程程序的Java程序员不理解隐式进行的线程之间通信的工作机制,很可能会遇到各种奇怪的内存可见性问题。

    Java内存模型

    上面讲到了Java线程之间的通信采用的是过共享内存模型,这里提到的共享内存模型指的就是Java内存模型(简称JMM),JMM决定一个线程对共享变量的写入何时对另一个线程可见。从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。

    这里写图片描述

    从上图来看,线程A与线程B之间如要通信的话,必须要经历下面2个步骤:

    1. 首先,线程A把本地内存A中更新过的共享变量刷新到主内存中去。
    2. 然后,线程B到主内存中去读取线程A之前已更新过的共享变量。 
    

    下面通过示意图来说明这两个步骤:
    这里写图片描述

    如上图所示,本地内存A和B有主内存中共享变量x的副本。假设初始时,这三个内存中的x值都为0。线程A在执行时,把更新后的x值(假设值为1)临时存放在自己的本地内存A中。当线程A和线程B需要通信时,线程A首先会把自己本地内存中修改后的x值刷新到主内存中,此时主内存中的x值变为了1。随后,线程B到主内存中去读取线程A更新后的x值,此时线程B的本地内存的x值也变为了1。

    从整体来看,这两个步骤实质上是线程A在向线程B发送消息,而且这个通信过程必须要经过主内存。JMM通过控制主内存与每个线程的本地内存之间的交互,来为java程序员提供内存可见性保证。

    上面也说到了,Java内存模型只是一个抽象概念,那么它在Java中具体是怎么工作的呢?为了更好的理解上Java内存模型工作方式,下面就JVM对Java内存模型的实现、硬件内存模型及它们之间的桥接做详细介绍。

    JVM对Java内存模型的实现

    在JVM内部,Java内存模型把内存分成了两部分:线程栈区和堆区,下图展示了Java内存模型在JVM中的逻辑视图:
    这里写图片描述
    JVM中运行的每个线程都拥有自己的线程栈,线程栈包含了当前线程执行的方法调用相关信息,我们也把它称作调用栈。随着代码的不断执行,调用栈会不断变化。

    线程栈还包含了当前方法的所有本地变量信息。一个线程只能读取自己的线程栈,也就是说,线程中的本地变量对其它线程是不可见的。即使两个线程执行的是同一段代码,它们也会各自在自己的线程栈中创建本地变量,因此,每个线程中的本地变量都会有自己的版本。

    所有原始类型(boolean,byte,short,char,int,long,float,double)的本地变量都直接保存在线程栈当中,对于它们的值各个线程之间都是独立的。对于原始类型的本地变量,一个线程可以传递一个副本给另一个线程,当它们之间是无法共享的。

    堆区包含了Java应用创建的所有对象信息,不管对象是哪个线程创建的,其中的对象包括原始类型的封装类(如Byte、Integer、Long等等)。不管对象是属于一个成员变量还是方法中的本地变量,它都会被存储在堆区。

    下图展示了调用栈和本地变量都存储在栈区,对象都存储在堆区:
    这里写图片描述
    一个本地变量如果是原始类型,那么它会被完全存储到栈区。
    一个本地变量也有可能是一个对象的引用,这种情况下,这个本地引用会被存储到栈中,但是对象本身仍然存储在堆区。

    对于一个对象的成员方法,这些方法中包含本地变量,仍需要存储在栈区,即使它们所属的对象在堆区。
    对于一个对象的成员变量,不管它是原始类型还是包装类型,都会被存储到堆区。

    Static类型的变量以及类本身相关信息都会随着类本身存储在堆区。

    堆中的对象可以被多线程共享。如果一个线程获得一个对象的应用,它便可访问这个对象的成员变量。如果两个线程同时调用了同一个对象的同一个方法,那么这两个线程便可同时访问这个对象的成员变量,但是对于本地变量,每个线程都会拷贝一份到自己的线程栈中。

    下图展示了上面描述的过程:
    这里写图片描述

    硬件内存架构

    不管是什么内存模型,最终还是运行在计算机硬件上的,所以我们有必要了解计算机硬件内存架构,下图就简单描述了当代计算机硬件内存架构:
    这里写图片描述

    现代计算机一般都有2个以上CPU,而且每个CPU还有可能包含多个核心。因此,如果我们的应用是多线程的话,这些线程可能会在各个CPU核心中并行运行。

    在CPU内部有一组CPU寄存器,也就是CPU的储存器。CPU操作寄存器的速度要比操作计算机主存快的多。在主存和CPU寄存器之间还存在一个CPU缓存,CPU操作CPU缓存的速度快于主存但慢于CPU寄存器。某些CPU可能有多个缓存层(一级缓存和二级缓存)。计算机的主存也称作RAM,所有的CPU都能够访问主存,而且主存比上面提到的缓存和寄存器大很多。

    当一个CPU需要访问主存时,会先读取一部分主存数据到CPU缓存,进而在读取CPU缓存到寄存器。当CPU需要写数据到主存时,同样会先flush寄存器到CPU缓存,然后再在某些节点把缓存数据flush到主存。

    Java内存模型和硬件架构之间的桥接

    正如上面讲到的,Java内存模型和硬件内存架构并不一致。硬件内存架构中并没有区分栈和堆,从硬件上看,不管是栈还是堆,大部分数据都会存到主存中,当然一部分栈和堆的数据也有可能会存到CPU寄存器中,如下图所示,Java内存模型和计算机硬件内存架构是一个交叉关系:
    这里写图片描述
    当对象和变量存储到计算机的各个内存区域时,必然会面临一些问题,其中最主要的两个问题是:

    1. 共享对象对各个线程的可见性
    2. 共享对象的竞争现象
    

    共享对象的可见性

    当多个线程同时操作同一个共享对象时,如果没有合理的使用volatile和synchronization关键字,一个线程对共享对象的更新有可能导致其它线程不可见。

    想象一下我们的共享对象存储在主存,一个CPU中的线程读取主存数据到CPU缓存,然后对共享对象做了更改,但CPU缓存中的更改后的对象还没有flush到主存,此时线程对共享对象的更改对其它CPU中的线程是不可见的。最终就是每个线程最终都会拷贝共享对象,而且拷贝的对象位于不同的CPU缓存中。

    下图展示了上面描述的过程。左边CPU中运行的线程从主存中拷贝共享对象obj到它的CPU缓存,把对象obj的count变量改为2。但这个变更对运行在右边CPU中的线程不可见,因为这个更改还没有flush到主存中:
    这里写图片描述
    要解决共享对象可见性这个问题,我们可以使用java volatile关键字。 Java’s volatile keyword. volatile 关键字可以保证变量会直接从主存读取,而对变量的更新也会直接写到主存。volatile原理是基于CPU内存屏障指令实现的,后面会讲到。

    竞争现象

    如果多个线程共享一个对象,如果它们同时修改这个共享对象,这就产生了竞争现象。

    如下图所示,线程A和线程B共享一个对象obj。假设线程A从主存读取Obj.count变量到自己的CPU缓存,同时,线程B也读取了Obj.count变量到它的CPU缓存,并且这两个线程都对Obj.count做了加1操作。此时,Obj.count加1操作被执行了两次,不过都在不同的CPU缓存中。

    如果这两个加1操作是串行执行的,那么Obj.count变量便会在原始值上加2,最终主存中的Obj.count的值会是3。然而下图中两个加1操作是并行的,不管是线程A还是线程B先flush计算结果到主存,最终主存中的Obj.count只会增加1次变成2,尽管一共有两次加1操作。
    这里写图片描述

    要解决上面的问题我们可以使用java synchronized代码块。synchronized代码块可以保证同一个时刻只能有一个线程进入代码竞争区,synchronized代码块也能保证代码块中所有变量都将会从主存中读,当线程退出代码块时,对所有变量的更新将会flush到主存,不管这些变量是不是volatile类型的。

    volatile和 synchronized区别

    详细请见 volatile和synchronized的区别

    支撑Java内存模型的基础原理

    指令重排序

    在执行程序时,为了提高性能,编译器和处理器会对指令做重排序。但是,JMM确保在不同的编译器和不同的处理器平台之上,通过插入特定类型的Memory Barrier来禁止特定类型的编译器重排序和处理器重排序,为上层提供一致的内存可见性保证。

    1. 编译器优化重排序:编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。
    2. 指令级并行的重排序:如果不存l在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。
    3. 内存系统的重排序:处理器使用缓存和读写缓冲区,这使得加载和存储操作看上去可能是在乱序执行。

    数据依赖性

    如果两个操作访问同一个变量,其中一个为写操作,此时这两个操作之间存在数据依赖性。
    编译器和处理器不会改变存在数据依赖性关系的两个操作的执行顺序,即不会重排序。

    as-if-serial

    不管怎么重排序,单线程下的执行结果不能被改变,编译器、runtime和处理器都必须遵守as-if-serial语义。

    内存屏障(Memory Barrier )

    上面讲到了,通过内存屏障可以禁止特定类型处理器的重排序,从而让程序按我们预想的流程去执行。内存屏障,又称内存栅栏,是一个CPU指令,基本上它是一条这样的指令:

    1. 保证特定操作的执行顺序。
    2. 影响某些数据(或则是某条指令的执行结果)的内存可见性。

    编译器和CPU能够重排序指令,保证最终相同的结果,尝试优化性能。插入一条Memory Barrier会告诉编译器和CPU:不管什么指令都不能和这条Memory Barrier指令重排序。

    Memory Barrier所做的另外一件事是强制刷出各种CPU cache,如一个Write-Barrier(写入屏障)将刷出所有在Barrier之前写入 cache 的数据,因此,任何CPU上的线程都能读取到这些数据的最新版本。

    这和java有什么关系?上面java内存模型中讲到的volatile是基于Memory Barrier实现的。

    如果一个变量是volatile修饰的,JMM会在写入这个字段之后插进一个Write-Barrier指令,并在读这个字段之前插入一个Read-Barrier指令。这意味着,如果写入一个volatile变量,就可以保证:

    1. 一个线程写入变量a后,任何线程访问该变量都会拿到最新值。
    2. 在写入变量a之前的写入操作,其更新的数据对于其他线程也是可见的。因为Memory Barrier会刷出cache中的所有先前的写入。

    happens-before

    从jdk5开始,java使用新的JSR-133内存模型,基于happens-before的概念来阐述操作之间的内存可见性。

    在JMM中,如果一个操作的执行结果需要对另一个操作可见,那么这两个操作之间必须要存在happens-before关系,这个的两个操作既可以在同一个线程,也可以在不同的两个线程中。

    与程序员密切相关的happens-before规则如下:

    1. 程序顺序规则:一个线程中的每个操作,happens-before于该线程中任意的后续操作。
    2. 监视器锁规则:对一个锁的解锁操作,happens-before于随后对这个锁的加锁操作。
    3. volatile域规则:对一个volatile域的写操作,happens-before于任意线程后续对这个volatile域的读。
    4. 传递性规则:如果 A happens-before B,且 B happens-before C,那么A happens-before C。

    注意:两个操作之间具有happens-before关系,并不意味前一个操作必须要在后一个操作之前执行!仅仅要求前一个操作的执行结果,对于后一个操作是可见的,且前一个操作按顺序排在后一个操作之前。

    参考文档 :
    1. http://www.infoq.com/cn/articles/java-memory-model-1
    2. http://www.jianshu.com/p/d3fda02d4cae

    展开全文
  • Tensorflow加载预训练模型和保存模型

    万次阅读 多人点赞 2017-11-10 19:07:16
    使用tensorflow过程中,训练结束后我们需要用到模型文件。有时候,我们可能也需要用到别人训练好的模型,并在这个基础上再次训练。这时候我们需要掌握如何操作这些模型数据。看完本文,相信你一定会有收获!

    最近看到一个巨牛的人工智能教程,分享一下给大家。教程不仅是零基础,通俗易懂,而且非常风趣幽默,像看小说一样!觉得太牛了,所以分享给大家。平时碎片时间可以当小说看,【点这里可以去膜拜一下大神的“小说”】

    使用tensorflow过程中,训练结束后我们需要用到模型文件。有时候,我们可能也需要用到别人训练好的模型,并在这个基础上再次训练。这时候我们需要掌握如何操作这些模型数据。看完本文,相信你一定会有收获!

    1 Tensorflow模型文件

    我们在checkpoint_dir目录下保存的文件结构如下:

    |--checkpoint_dir
    |    |--checkpoint
    |    |--MyModel.meta
    |    |--MyModel.data-00000-of-00001
    |    |--MyModel.index
    

    1.1 meta文件

    MyModel.meta文件保存的是图结构,meta文件是pb(protocol buffer)格式文件,包含变量、op、集合等。

    1.2 ckpt文件

    ckpt文件是二进制文件,保存了所有的weights、biases、gradients等变量。在tensorflow 0.11之前,保存在**.ckpt**文件中。0.11后,通过两个文件保存,如:

    MyModel.data-00000-of-00001
    MyModel.index
    

    1.3 checkpoint文件

    我们还可以看,checkpoint_dir目录下还有checkpoint文件,该文件是个文本文件,里面记录了保存的最新的checkpoint文件以及其它checkpoint文件列表。在inference时,可以通过修改这个文件,指定使用哪个model

    2 保存Tensorflow模型

    tensorflow 提供了tf.train.Saver类来保存模型,值得注意的是,在tensorflow中,变量是存在于Session环境中,也就是说,只有在Session环境下才会存有变量值,因此,保存模型时需要传入session:

    saver = tf.train.Saver()
    saver.save(sess,"./checkpoint_dir/MyModel")
    

    看一个简单例子:

    import tensorflow as tf
    
    w1 = tf.Variable(tf.random_normal(shape=[2]), name='w1')
    w2 = tf.Variable(tf.random_normal(shape=[5]), name='w2')
    saver = tf.train.Saver()
    sess = tf.Session()
    sess.run(tf.global_variables_initializer())
    saver.save(sess, './checkpoint_dir/MyModel')
    

    执行后,在checkpoint_dir目录下创建模型文件如下:

    checkpoint
    MyModel.data-00000-of-00001
    MyModel.index
    MyModel.meta
    

    另外,如果想要在1000次迭代后,再保存模型,只需设置global_step参数即可:

    saver.save(sess, './checkpoint_dir/MyModel',global_step=1000)
    

    保存的模型文件名称会在后面加-1000,如下:

    checkpoint
    MyModel-1000.data-00000-of-00001
    MyModel-1000.index
    MyModel-1000.meta
    

    在实际训练中,我们可能会在每1000次迭代中保存一次模型数据,但是由于图是不变的,没必要每次都去保存,可以通过如下方式指定不保存图:

    saver.save(sess, './checkpoint_dir/MyModel',global_step=step,write_meta_graph=False)
    

    另一种比较实用的是,如果你希望每2小时保存一次模型,并且只保存最近的5个模型文件:

    tf.train.Saver(max_to_keep=5, keep_checkpoint_every_n_hours=2)
    

    注意:tensorflow默认只会保存最近的5个模型文件,如果你希望保存更多,可以通过max_to_keep来指定

    如果我们不对tf.train.Saver指定任何参数,默认会保存所有变量。如果你不想保存所有变量,而只保存一部分变量,可以通过指定variables/collections。在创建tf.train.Saver实例时,通过将需要保存的变量构造list或者dictionary,传入到Saver中:

    import tensorflow as tf
    w1 = tf.Variable(tf.random_normal(shape=[2]), name='w1')
    w2 = tf.Variable(tf.random_normal(shape=[5]), name='w2')
    saver = tf.train.Saver([w1,w2])
    sess = tf.Session()
    sess.run(tf.global_variables_initializer())
    saver.save(sess, './checkpoint_dir/MyModel',global_step=1000)
    

    3 导入训练好的模型

    在第1小节中我们介绍过,tensorflow将图和变量数据分开保存为不同的文件。因此,在导入模型时,也要分为2步:构造网络图和加载参数

    3.1 构造网络图

    一个比较笨的方法是,手敲代码,实现跟模型一模一样的图结构。其实,我们既然已经保存了图,那就没必要在去手写一次图结构代码。

    saver=tf.train.import_meta_graph('./checkpoint_dir/MyModel-1000.meta')
    

    上面一行代码,就把图加载进来了

    3.2 加载参数

    仅仅有图并没有用,更重要的是,我们需要前面训练好的模型参数(即weights、biases等),本文第2节提到过,变量值需要依赖于Session,因此在加载参数时,先要构造好Session:

    import tensorflow as tf
    with tf.Session() as sess:
      new_saver = tf.train.import_meta_graph('./checkpoint_dir/MyModel-1000.meta')
      new_saver.restore(sess, tf.train.latest_checkpoint('./checkpoint_dir'))
    

    此时,W1和W2加载进了图,并且可以被访问:

    import tensorflow as tf
    with tf.Session() as sess:    
        saver = tf.train.import_meta_graph('./checkpoint_dir/MyModel-1000.meta')
        saver.restore(sess,tf.train.latest_checkpoint('./checkpoint_dir'))
        print(sess.run('w1:0'))
    ##Model has been restored. Above statement will print the saved value
    

    执行后,打印如下:

    [ 0.51480412 -0.56989086]
    

    4 使用恢复的模型

    前面我们理解了如何保存和恢复模型,很多时候,我们希望使用一些已经训练好的模型,如prediction、fine-tuning以及进一步训练等。这时候,我们可能需要获取训练好的模型中的一些中间结果值,可以通过graph.get_tensor_by_name('w1:0')来获取,注意w1:0是tensor的name。

    假设我们有一个简单的网络模型,代码如下:

    import tensorflow as tf
    
    
    w1 = tf.placeholder("float", name="w1")
    w2 = tf.placeholder("float", name="w2")
    b1= tf.Variable(2.0,name="bias") 
    
    #定义一个op,用于后面恢复
    w3 = tf.add(w1,w2)
    w4 = tf.multiply(w3,b1,name="op_to_restore")
    sess = tf.Session()
    sess.run(tf.global_variables_initializer())
    
    #创建一个Saver对象,用于保存所有变量
    saver = tf.train.Saver()
    
    #通过传入数据,执行op
    print(sess.run(w4,feed_dict ={w1:4,w2:8}))
    #打印 24.0 ==>(w1+w2)*b1
    
    #现在保存模型
    saver.save(sess, './checkpoint_dir/MyModel',global_step=1000)
    

    接下来我们使用graph.get_tensor_by_name()方法来操纵这个保存的模型。

    import tensorflow as tf
    
    sess=tf.Session()
    #先加载图和参数变量
    saver = tf.train.import_meta_graph('./checkpoint_dir/MyModel-1000.meta')
    saver.restore(sess, tf.train.latest_checkpoint('./checkpoint_dir'))
    
    
    # 访问placeholders变量,并且创建feed-dict来作为placeholders的新值
    graph = tf.get_default_graph()
    w1 = graph.get_tensor_by_name("w1:0")
    w2 = graph.get_tensor_by_name("w2:0")
    feed_dict ={w1:13.0,w2:17.0}
    
    #接下来,访问你想要执行的op
    op_to_restore = graph.get_tensor_by_name("op_to_restore:0")
    
    print(sess.run(op_to_restore,feed_dict))
    #打印结果为60.0==>(13+17)*2
    

    注意:保存模型时,只会保存变量的值,placeholder里面的值不会被保存

    如果你不仅仅是用训练好的模型,还要加入一些op,或者说加入一些layers并训练新的模型,可以通过一个简单例子来看如何操作:

    import tensorflow as tf
    
    sess = tf.Session()
    # 先加载图和变量
    saver = tf.train.import_meta_graph('my_test_model-1000.meta')
    saver.restore(sess, tf.train.latest_checkpoint('./'))
    
    # 访问placeholders变量,并且创建feed-dict来作为placeholders的新值
    graph = tf.get_default_graph()
    w1 = graph.get_tensor_by_name("w1:0")
    w2 = graph.get_tensor_by_name("w2:0")
    feed_dict = {w1: 13.0, w2: 17.0}
    
    #接下来,访问你想要执行的op
    op_to_restore = graph.get_tensor_by_name("op_to_restore:0")
    
    # 在当前图中能够加入op
    add_on_op = tf.multiply(op_to_restore, 2)
    
    print (sess.run(add_on_op, feed_dict))
    # 打印120.0==>(13+17)*2*2
    
    

    如果只想恢复图的一部分,并且再加入其它的op用于fine-tuning。只需通过graph.get_tensor_by_name()方法获取需要的op,并且在此基础上建立图,看一个简单例子,假设我们需要在训练好的VGG网络使用图,并且修改最后一层,将输出改为2,用于fine-tuning新数据:

    ......
    ......
    saver = tf.train.import_meta_graph('vgg.meta')
    # 访问图
    graph = tf.get_default_graph() 
     
    #访问用于fine-tuning的output
    fc7= graph.get_tensor_by_name('fc7:0')
     
    #如果你想修改最后一层梯度,需要如下
    fc7 = tf.stop_gradient(fc7) # It's an identity function
    fc7_shape= fc7.get_shape().as_list()
    
    new_outputs=2
    weights = tf.Variable(tf.truncated_normal([fc7_shape[3], num_outputs], stddev=0.05))
    biases = tf.Variable(tf.constant(0.05, shape=[num_outputs]))
    output = tf.matmul(fc7, weights) + biases
    pred = tf.nn.softmax(output)
    
    # Now, you run this with fine-tuning data in sess.run()
    

    Reference

    http://cv-tricks.com/tensorflow-tutorial/save-restore-tensorflow-models-quick-complete-tutorial/

    展开全文
  • jvm 内存模型

    万次阅读 多人点赞 2019-08-15 14:28:51
    2. jvm 内存模型划分 根据JVM规范,JVM 内存共分为虚拟机栈,堆,方法区,程序计数器,本地方法栈五个部分。 程序计数器(线程私有): 是当前线程锁执行字节码的行号治时期,每条线程都有一个独立的程序计数器...

    二. jvm 内存模型划分

    根据JVM规范,JVM 内存共分为虚拟机栈,堆,方法区,程序计数器,本地方法栈五个部分。
    这里写图片描述

    程序计数器(线程私有):
    是当前线程锁执行字节码的行号治时期,每条线程都有一个独立的程序计数器,这类内存也称为“线程私有”的内存。正在执行java方法的话,计数器记录的是虚拟机字节码指令的地址(当前指令的地址)。如果是Natice方法,则为空。

    java 虚拟机栈
    也是线程私有的。
    每个方法在执行的时候也会创建一个栈帧,存储了局部变量,操作数,动态链接,方法返回地址。
    每个方法从调用到执行完毕,对应一个栈帧在虚拟机栈中的入栈和出栈。
    通常所说的栈,一般是指在虚拟机栈中的局部变量部分。
    局部变量所需内存在编译期间完成分配,
    如果线程请求的栈深度大于虚拟机所允许的深度,则StackOverflowError。
    如果虚拟机栈可以动态扩展,扩展到无法申请足够的内存,则OutOfMemoryError。
    本地方法栈(线程私有)
    和虚拟机栈类似,主要为虚拟机使用到的Native方法服务。也会抛出StackOverflowError 和OutOfMemoryError。

    Java堆(线程共享)
    被所有线程共享的一块内存区域,在虚拟机启动的时候创建,用于存放对象实例。
    对可以按照可扩展来实现(通过-Xmx 和-Xms 来控制)
    当队中没有内存可分配给实例,也无法再扩展时,则抛出OutOfMemoryError异常。
    方法区(线程共享)
    被所有方法线程共享的一块内存区域。
    用于存储已经被虚拟机加载的类信息,常量,静态变量等。
    这个区域的内存回收目标主要针对常量池的回收和堆类型的卸载。

    3.jvm 1.8 内存区域划分
    这里写图片描述

    程序计数器
    每个线程一块内存,指向当前正在执行的字节码的行号。如果当前线程是native方法,则其值为null。

    ps(程序计数器内存划分)
    因为处理器在一个确定是时刻只会执行一个线程中的指令,线程切换后,是通过计数器来记录执行痕迹的,因而可以看出,程序计数器是每个线程私有的。
    如果执行的是java方法,那么记录的是正在执行的虚拟机字节码指令的地址的地址,如果是native方法,计数器的值为空(undefined)。

    Java虚拟机栈

    这里写图片描述

    ps: 虚拟机栈中的名词解释

    局部变量表:
    存放编译期可知的各种基本数据类型、对象引用类型和returnAddress类型(指向一条字节码指令的地址:函数返回地址)。
    long、double占用两个局部变量控件Slot。
    局部变量表所需的内存空间在编译期确定,当进入一个方法时,方法在栈帧中所需要分配的局部变量控件是完全确定的,不可动态改变大小。
    异常:线程请求的栈帧深度大于虚拟机所允许的深度—StackOverFlowError,如果虚拟机栈可以动态扩展(大部分虚拟机允许动态扩展,也可以设置固定大小的虚拟机栈),但是无法申请到足够的内存—OutOfMemorError。

    操作数栈:
    后进先出LIFO,最大深度由编译期确定。栈帧刚建立使,操作数栈为空,执行方法操作时,操作数栈用于存放JVM从局部变量表复制的常量或者变量,提供提取,及结果入栈,也用于存放调用方法需要的参数及接受方法返回的结果。
    操作数栈可以存放一个jvm中定义的任意数据类型的值。
    在任意时刻,操作数栈都一个固定的栈深度,基本类型除了long、double占用两个深度,其它占用一个深度
    动态连接:
    每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接。Class文件的常量池中存在有大量的符号引用,字节码中的方法调用指令就以常量池中指向方法的符号引用为参数。这些符号引用,一部分会在类加载阶段或第一次使用的时候转化为直接引用(如final、static域等),称为静态解析,另一部分将在每一次的运行期间转化为直接引用,这部分称为动态连接。

    方法返回地址:
    当一个方法被执行后,有两种方式退出该方法:执行引擎遇到了任意一个方法返回的字节码指令或遇到了异常,并且该异常没有在方法体内得到处理。无论采用何种退出方式,在方法退出之后,都需要返回到方法被调用的位置,程序才能继续执行。方法返回时可能需要在栈帧中保存一些信息,用来帮助恢复它的上层方法的执行状态。一般来说,方法正常退出时,调用者的PC计数器的值就可以作为返回地址,栈帧中很可能保存了这个计数器值,而方法异常退出时,返回地址是要通过异常处理器来确定的,栈帧中一般不会保存这部分信息。
    方法退出的过程实际上等同于把当前栈帧出栈,因此退出时可能执行的操作有:恢复上层方法的局部变量表和操作数栈,如果有返回值,则把它压入调用者栈帧的操作数栈中,调整PC计数器的值以指向方法调用指令后面的一条指令。


    堆时JVM内存占用最大,管理最复杂的一个区域。唯一的途径就是存放对象实例:所有的对象实例以及数组都在堆上进行分配。jdk1.7以后,字符串常量从永久代中剥离出来,存放在堆中。堆具有进一步的内存划分。按照GC分代手机角度划分
    这里写图片描述
    老年代:2/3的堆空间
    年轻代:1/3的堆空间
    eden区:8/10 的年轻代
    survivor0: 1/10 的年轻代
    survivor1:1/10的年轻代

    元数据区域
    元数据区域取代了1.7版本及以前的永久代。元数据和永久代本质上都时方法区的实现。方法区皴法虚拟机加载的类型西,静态变量,常量数据。
    参数设置:-XX:MetaspaceSize=18m
    -XX:MaxMetaspaceSize=60m
    直接内存
    java.nio 中使用DirectBuffer相关使用(此处未完待续。。。。。。。。。)

    展开全文
  • 永磁同步电机矢量控制(一)——数学模型

    万次阅读 多人点赞 2019-05-17 09:51:21
    导师研究的课题是永磁同步... 1、永磁同步电机的数学模型 (参考于解小刚、陈进采用Id=0永磁同步电机矢量控制文章) 永磁同步电机是一个非线性系统,具有多变量、强耦合的特点。我们对其分析的时候有以下假设: ...

    注:
    1:此为永磁同步控制系列文章之一,应大家的要求,关于永磁同步矢量控制的系列文章已经在主页置顶,大家可以直接去主页里面查阅,希望能给大家带来帮助,谢谢。
    2:矢量控制的六篇文章后。弱磁、MTPA、位置控制系列讲解已经补充,也放在主页了,请大家查阅。
    3: 恰饭一下,也做了一套较为详细教程放在置顶了,内含基本双闭环、MTPA、弱磁、三闭环、模糊PI等基本控制优化策略,也将滑模,MRAS等无速度控制课题整理完成,请大家查看_
    **
    4、文章对应资料附件放在了文章末尾

    导师研究的课题是永磁同步电机的控制,首先给我安排的任务就是将其矢量控制系统仿真搭建出来。本文记录矢量控制系统学习过程。因为是初学我的理解可能不够,其中每个内容的出处都会在文章内标注出来,大家可以参考原文原著。

    1、永磁同步电机的数学模型 (参考于解小刚、陈进采用Id=0永磁同步电机矢量控制文章)
    永磁同步电机是一个非线性系统,具有多变量、强耦合的特点。我们对其分析的时候有以下假设:

    • 忽略铁芯饱和,不计涡流和磁滞损耗

    • 忽略换相过程中的电枢反应

    • 转子上无阻尼绕组,永磁体无阻尼作用

    • 永磁体产生的磁场和三相绕组产生的感应磁场呈正弦分布

    • 定子绕组电流在气隙中只产生正弦分布的磁势,无高次谐波

    • 按照电动机应用建模
      在此理想条件下:
      1.1 永磁同步电机在三相静止坐标系下定子电压方程:(下图有误,定子磁链要求个导)
      这里写图片描述
      式中Rs为电枢电阻,ψa ψb ψc分别为abc三相磁链,ia ib ic 分别为其 abc三相的相电流。
      1.2 三相静止坐标系下磁链方程
      这里写图片描述
      其中Laa、Lbb、Lcc为各相绕组自感,且Laa=Lbb=Lcc,式中Mab等为绕组之间互感且均相等。ψf是永磁体磁链,θ为转子N极和a相轴线之间的夹角。
      经过CLARK和PARK左边变换后,得到其在dq坐标系下的数学模型:
      1.3 dq坐标系下电压方程
      这里写图片描述
      其中ud、uq为dq轴电压,id、iq为dq轴电流,ψd、ψq为dq轴磁链,Ld、Lq为dq轴电感,we为转速。
      1.4 dq坐标系下磁链方程
      这里写图片描述
      1.5 转矩方程
      在这里插入图片描述
      从上1.5中转矩方程可以看出,电磁转矩由两个部分组成,第一项是永磁体和定子绕组磁链之间相互作用产生,第二项则是由磁阻变化而产生的。这里我们需要区分一下凸极和隐极电机的区别,隐极电机由于Lq=Ld,所以磁阻变化转矩是凸极电机特有的,我们在搭建仿真的时候也需要注意这的电机类型。

      小结:
      永磁同步电机的数学模型解释了其内部构成,有助于我们设计控制策略,我们进行坐标变换和PI参数整定时都需要对其数学模型进行分析,很重要,很重要,很重要,说三遍!!!

    需要文章资料与仿真模型的同学请博客下评论留一下邮箱,看到就会发过去。
    整理不易,希望大家帮忙点个赞呀,谢谢啦~_

    后续文章链接:

    永磁同步电机矢量控制到无速度传感器控制学习教程(PMSM)
    永磁同步电机矢量控制(二)——控制原理与坐标变换推导
    永磁同步电机矢量控制(三)——电流环转速环 PI 参数整定
    永磁同步电机矢量控制(四)——simulink仿真搭建
    永磁同步电机矢量控制(五)——波形记录及其分析
    永磁同步电机矢量控制(六)——MTPA最大转矩电流比控制
    永磁同步电机矢量控制(七)——基于id=0的矢量控制的动态解耦策略
    永磁同步电机矢量控制(八)——弱磁控制(超前角弱磁)
    永磁同步电机矢量控制(九)——三闭环位置控制系统
    永磁同步电机矢量控制(十)——PMSM最优效率(最小损耗)控制策略

    展开全文
  • PyTorch 实战(模型训练、模型加载、模型测试)

    万次阅读 多人点赞 2019-07-30 10:25:18
    保存模型->加载模型->测试模型 自定义数据集 参考我的上一篇博客:自定义数据集处理 数据加载 默认小伙伴有对深度学习框架有一定的了解,这里就不做过多的说明了。 好吧,还是简单的说一下吧: 我们在做好...
  • 权限系统与RBAC模型概述[绝对经典]

    万次阅读 多人点赞 2017-06-30 10:12:38
    当时凭着自己的逻辑设计出了一套权限管理模型,基本原理与RBAC非常相似,只是过于简陋。当时google了一些权限管理的资料,从中了解到早就有了RBAC这个东西。可惜一直没狠下心来学习。 更详细的RBAC模型非常复杂。...
  • [NLP自然语言处理]谷歌BERT模型深度解析

    万次阅读 多人点赞 2018-10-15 17:49:18
    我的机器学习教程「美团」算法工程师带你入门机器学习 已经开始更新了,欢迎大家订阅~ 任何关于算法、编程、AI行业知识或博客内容的问题,可以随时扫码关注公众号「图灵的猫」,加入”...BERT模型代码已经发布,...
  • 比如我要往模型的胸部丢一个鸡蛋,然后鸡蛋碎了,在胸部炸开一个鸡蛋,留下一点东西,然后改变胸部的模型样子
  • 本文总结了常用的数学模型方法和它们的主要用途,主要包括数学和统计上的建模方法,关于在数学建模中也挺常用的机器学习算法暂时不作补充,以后有时间就补。至于究竟哪个模型更好,需要用数据来验证,还有求解方法也...
  • 模型评估与模型选择

    千次阅读 多人点赞 2021-04-03 20:42:40
    我们来学习一下模型评估与选择的相关问题。在进入正式的学习之前,请你思考一下:我们为什么要做模型的评估与选择呢? 导论 在某个特定的数据场景下,我们事先并不知道什么样的模型可以近似刻画数据的规律。我们的...
  • 基于CNN的垃圾分类模型

    万次阅读 多人点赞 2018-01-10 15:44:21
    基于TensorFlow和Keras的垃圾分类模型 本篇博客主要介绍基于TensorFlow和Keras实现垃圾分类模型,目前是一篇占坑的博客,由于该项目目前用于参加比赛,因此暂时不能提供代码,感兴趣的可以私信我一起交流,识别...
  • 三大模型 1、预测模型 2、优化模型 3、评价模型 数学建模的十大常用算法 预测模型:神经网络预测、灰色预测、拟合插值预测(线性回归)、时间序列预测、马尔科夫链预测、微分方程预测、Logistic 模型等等。 应用...
  • 一、瀑布模型 1.1什么是瀑布模型 1.2特点 1.3优缺点 1.4客户需求 二、快速原型模型 2.1什么是快速原型模型 2.2优缺点 2.3快速原型模型的思想产生、原理及运用方式 2.4类型 2.5开发步骤 三、增量模型 3.1...
  • 层次数据模型     定义:层次数据模型是用树状<层次>结构来组织数据的数据模型。     满足下面两个条件的基本层次联系的集合为层次模型     1. 有且只有一个结点没有双亲结点,这个结点称为根结点...
  • 经典线性模型自变量的线性预测就是因变量的估计值。 广义线性模型:自变量的线性预测的函数是因变量的估计值。常见的广义线性模型有:probit模型、poisson模型、对数线性模型等等。对数线性模型里有:logistic ...
  • 数学建模四大模型总结

    万次阅读 多人点赞 2019-06-13 15:31:53
    文章目录数学建模四大模型总结1 、优化模型1.1 数学规划模型1.2 微分方程组模型1.3 图论与网络优化问题1.4 概率模型1.5 组合优化经典问题现代优化算法:禁忌搜索;模拟退火;遗传算法;人工神经网络2、分类模型2.1 ...
  • 2.1 维度模型 2.1.1 星型模型 2.1.2 雪花模型 2.1.3星座模型 2.2 范式模型 2.3 Data Vault模型 2.4 Anchor模型
  • 混淆矩阵是ROC曲线绘制的基础,同时它也是衡量分类型模型准确度中最基本,最直观,计算最简单的方法。 一句话解释版本: 混淆矩阵就是分别统计分类模型归错类,归对类的观测值个数,然后把结果放在一个表里展示...
  • Python实现向量自回归(VAR)模型——完整步骤

    万次阅读 多人点赞 2019-02-01 15:20:14
    以金融价格为例,传统的时间序列模型比如ARIMA,ARIMA-GARCH等,只分析价格自身的变化,模型的形式为: 其中称为自身的滞后项。 但是VAR模型除了分析自身滞后项的影响外,还分析其他相关因素的滞后项对未来值产生...
  • pytorch 加载(.pth)格式的模型

    万次阅读 多人点赞 2018-12-28 22:42:02
    有一些非常流行的网络如 resnet、squeezenet、densenet等在pytorch里面都有,包括网络结构和训练好的模型。 pytorch自带模型网址:...按官网加载预训练好的模型: import torchvision.models...
  • 测试模型
  • 文章目录0. 软件的生命周期1. 瀑布模型2. 螺旋模型3. 迭代模型4. 增量模型5....  瀑布模型是最早出现的软件开发模型,是所有其他软件开发模型的基础框架。与软件的生命周期不同的是,它缺少了软...
  • 数据模型的概念2.两大类数据模型客观对象的抽象过程---两步抽象3.数据模型的组成要素(1)数据结构(2)数据操作(3)数据的完整性约束条件4.概念模型(1)用途与基本要求(2) 信息世界中的基本概念(3)两个实体型之间的联系①...
  • Hadoop的架构模型

    万次阅读 2020-10-06 11:32:52
    Hadoop的架构模型(1.x,2.x的各种架构模型介绍)1.x的版本架构模型介绍2.x的版本架构模型介绍 1.x的版本架构模型介绍 文件系统核心模块: NameNode:集群当中的主节点,主要用于管理集群当中的各种数据 ...
  • 时间序列模型 (一):模型概述

    万次阅读 多人点赞 2019-04-21 21:47:12
    时间序列模型 (一):模型概述 时间序列模型 (二):移动平均法 时间序列模型 (三):指数平滑法 时间序列模型 (四):差分指数平滑法、 自适应滤波法v 时间序列模型 (五): 趋势外推预测方法 时间序列...
  • Apollo代码学习(二)—车辆运动学模型

    万次阅读 多人点赞 2018-10-11 20:53:13
    Apollo代码学习—车辆运动学与动力学模型前言车辆模型车辆运动学模型车辆动力学模型 Apollo(阿波罗)是一个开放的、完整的、安全的自动驾驶平台,以灵活和高性能的技术架构,为全自动驾驶提供支持。 前言 要...
  • 文章目录一 项目展示二 依赖环境与导读2.1依赖环境2.2 本文导读三 模型训练3.1 新手也能做对的教学3.2 高手也爱的黑科技:无损的半精度模型模型优化4.1 Openvino 介绍 可以参见这篇博客4.2 Openvino安装4.3 Open...
  • 生成模型与判别模型

    万次阅读 多人点赞 2012-11-17 23:24:42
    生成模型与判别模型 zouxy09@qq.com http://blog.csdn.net/zouxy09  一直在看论文的过程中遇到这个问题,折腾了不少时间,然后是下面的一点理解,不知道正确否。若有错误,还望各位前辈不吝指正,以免小弟一错再...
  • 深度学习:transformer模型

    万次阅读 多人点赞 2018-12-10 21:59:24
    Google于2017年6月发布在arxiv上的一篇文章《Attention is all you need》,提出解决sequence to sequence问题的transformer模型,用全attention的结构代替了lstm,抛弃了之前传统的encoder-decoder模型必须结合cnn...
  • ER模型详解-陈氏模型

    万次阅读 2016-03-25 19:48:00
    http://blog.163.com/magicc_love/blog/static/18585366220142125836878/Entity Relationship Model - ER模型 - 实体关系模型 1976年Peter Chen首次提出了Entity Relationship Modeling(实体关系建模)概念,并...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,188,399
精华内容 1,275,359
关键字:

模型