精华内容
下载资源
问答
  • std::string> object_ids; object_ids.push_back(collision_object.id); planning_scene_interface.removeCollisionObjects(object_ids); visual_tools.publishText(text_pose, "Object removed", rvt::WHITE, ...
  • C++ reverse函数源码解析

    千次阅读 2019-05-28 12:56:27
    逆序(反转)无论是在C或是C++中用的都特别多,常用于数组,字符串,容器等,其本身的函数参数也不复杂。 标准C中是没有recerse()函数的,这是C++的一个新增函数,使用需要包含头文件 #include <algorithm> ...

    逆序(反转)无论是在C或是C++中用的都特别多,常用于数组,字符串,容器等,其本身的函数参数也不复杂。

    标准C中是没有recerse()函数的,这是C++的一个新增函数,使用需要包含头文件

    #include <algorithm>
    

    reverse函数用于反转在[first,last)范围内的顺序(包括first指向的元素,不包括last指向的元素),reverse函数没有返回值

    template <class BidirectionalIterator>
    void reverse (BidirectionalIterator first,BidirectionalIterator last);
    

    例如,交换vector容器中元素的顺序

    vector<int> v = {5,4,3,2,1};
    reverse(v.begin(),v.end());//v的值为1,2,3,4,5
    

    还有string类的字符串

    string str="www.mathor.top";
    reverse(str.begin(),str.end());//str结果为pot.rohtam.wwww
    

    给出函数原型,该函数等价于通过调用iter_swap来交换元素位置

    template <class BidirectionalIterator>
    void reverse (BidirectionalIterator first, BidirectionalIterator last)
    {
        while ((first!=last)&&(first!=--last))
        {
            std::iter_swap (first,last);
            ++first;
        }
    }
    

    最后追加一个完整例子:

    #include <iostream>
    #include <algorithm>
    #include <vector>
    
    using namespace std;
    template <class BidirectionalIterator>
    void my_reverse (BidirectionalIterator first, BidirectionalIterator last)
    {
      while ((first!=last)&&(first!=--last)) {
        std::iter_swap (first,last);
        ++first;
      }
    }
    
    int main()
    {
        int myints[] = {1,2,3,1};
        int len = sizeof(myints)/sizeof(int);
        vector<int> vec(myints, myints + len);
        my_reverse(vec.begin(), vec.end());
        for(int x: vec)
            cout<<x<<",";
    
        return 0;
    }
    
    

    参考:C++ reverse函数的用法

    展开全文
  • std::string源码详细解析

    千次阅读 2018-11-29 09:45:17
    查看std::string 类型C++源码,std::string类来源于typedef basic_string&lt;char, char_traits&lt;char&gt;, allocator&lt;char&gt; &gt; string; basic_string类型的继承关系如下: ...

    1.std::string简介

    查看std::string 类型C++源码,std::string类来源于typedef basic_string<char, char_traits<char>, allocator<char> > string;

    basic_string类型的继承关系如下:

    class basic_string à class _String_alloc à class _String_val à struct _Container_base12;

    std::string的成员变量是:

    1)struct _Container_base12结构中的_Container_proxy *_Myproxy

    _Container_proxy结构定义如下:

    struct _Container_proxy

           {     // store head of iterator chain and back pointer

           _Container_proxy()

                  : _Mycont(0), _Myfirstiter(0)

                  {     // construct from pointers

                  }

           const _Container_base12 *_Mycont;

           _Iterator_base12 *_Myfirstiter;

           };

    这个结构体非常类似于一个链表,存储不同的_Container_proxy对象的指针,管理的操作是标准STL中的迭代器对象。

    2)class _String_val中的联合体_Bx

    union _Bxty

                  {     // storage for small buffer or pointer to larger one

                  value_type _Buf[_BUF_SIZE];

                  pointer _Ptr;

                  char _Alias[_BUF_SIZE];     // to permit aliasing

                  } _Bx;

           并且在class _String_val中申明了

    enum

                  {     // length of internal buffer, [1, 16]

                  _BUF_SIZE = 16 / sizeof (value_type) < 1 ? 1

                         : 16 / sizeof (value_type)};

           value_type为char类型,所以_BUF_SIZE为16。

    3)class _String_val中的size_type _Mysize

    4)class _String_val中的size_type _Myres

    5)std::string对象占内存大小

    上面已经了解了std::string类型存在4个继承的成员,首先是_Myproxy指针对象,在32位环境下,占4字节大小;再者是_Bx联合体对象,联合体占内存大小取决于它所有的成员中占用空间最大的一个成员的大小,应该占16字节;另外_Mysize与_Myres分别占用4字节大小,std::string对象大小为28字节。

    【代码验证】

    #include <string>
    #include <iostream>
    int main()
    {
    	std::cout << sizeof(std::string) << std::endl;
    	return 0;
    }
    

    结果:28

    6)std::string对象内存信息

    【代码验证】

    #include <string>
    #include <iostream>
    int main()
    {
    	std::string str = "123456789";	 // 1
    	return 0;	                 // 2
    }
    

    上述代码执行到步骤2时,查看VS局部变量窗口,如下图1,可以看到str对象的起始地址为0x0045FE98;

    图1 std::string对象局部变量信息

    查看str对象内存信息,如下图2:

    图2 std::string对象内存信息

    图2可以看出std::string成员的内存分布,_Myporxy的内存地址为0x2500c8,字符串内容就保存在str对象的联合体_Buf[_BUF_SIZE]对象中,字符串的大小为_Mysize=9,因为需要预留’\0’结尾字符一个字节,当前总共可用的字符串空间为_Myres=15。

    注意:windos7系统采用的是小端模式的内存结构。

    2.存储字符串机制

    在std::string的简介中,介绍了联合体成员_Bx,而字符串存储和它息息相关,通过分析和调试std::string源码,可以知道,在申明std::string对象时,构造函数会调用

    void _Tidy(bool _Built = false, size_type _Newsize = 0)成员函数,使

    this->_Myres = this->_BUF_SIZE - 1;

    即对象的初始_Myres值为15,那么在首次赋值字符串给对象时,会调用

    _Myt& assign(const _Elem *_Ptr, size_type _Count)

                  {     // assign [_Ptr, _Ptr + _Count)

     #if _ITERATOR_DEBUG_LEVEL == 2

                  if (_Count != 0)

                         _DEBUG_POINTER(_Ptr);

     #endif /* _ITERATOR_DEBUG_LEVEL == 2 */

                  if (_Inside(_Ptr))

                         return (assign(*this,

                                _Ptr - this->_Myptr(), _Count));   // substring

                  if (_Grow(_Count))

                         {     // make room and assign new stuff

                         _Traits::copy(this->_Myptr(), _Ptr, _Count);

                         _Eos(_Count);

                         }

                  return (*this);

                  }

    成员函数,在深度执行调用过程过程中,

    bool _Grow(size_type _Newsize, bool _Trim = false)函数中会执行

    if (this->_Myres < _Newsize)

           _Copy(_Newsize, this->_Mysize);      // reallocate to grow

    这里判断字符串的大小是否大于_Myres;

    1)当字符串大小小于_Myres时,不会执行

    _Copy(_Newsize, this->_Mysize); // reallocate to grow

    会返回到在Myt& assign(const _Elem *_Ptr, size_type _Count)函数中,继续如下调用:

    _Traits::copy(this->_Myptr(), _Ptr, _Count);

    其中

    value_type *_Myptr()

                  {     // determine current pointer to buffer for mutable string

                  return (this->_BUF_SIZE <= this->_Myres

                         ? _STD addressof(*this->_Bx._Ptr)

                         : this->_Bx._Buf);

                  }

    会返回对象中可用于存储字符串的首地址,这里的

    this->_BUF_SIZE <= this->_Myres

    条件不成立,返回的是_Buf[_BUF_SIZE]数组地址,_Ptr为字符串的临时地址,_Count为字符串的大小。

    该操作会调用

    void *memcpy(void *dest, const void *src, size_t n);

    直接将字符串拷贝到_Bx联合体的_Buf[_BUF_SIZE]数组中存储。

    可以参考std::string简介中的代码验证以及str对象的内存信息。

    2)当字符串大小大于等于_Myres时,会先执行

    _Copy(_Newsize, this->_Mysize); // reallocate to grow

    即调用void _Copy(size_type _Newsize, size_type _Oldlen)函数去执行

    _Ptr = this->_Getal().allocate(_Newres + 1);

    再往深的调用中会调用_Ty *_Allocate(size_t _Count, _Ty *)该函数去执行

    _Ptr = ::operator new(_Count * sizeof (_Ty))

    也就申请了新的存储空间,并且该内存大小是_BUF_SIZE的整数倍,并且该内存大小大于等于字符串大小。

    再回到void _Copy(size_type _Newsize, size_type _Oldlen)函数中执行

    this->_Getal().construct(&this->_Bx._Ptr, _Ptr);

    将申请到的_Ptr新内存地址放置到_Bx._Ptr中。

    接着执行

    this->_Myres = _Newres;

    更新对象可用的内存空间大小。

    【代码验证】

    #include <string>
    #include <iostream>
    int main()
    {
    	std::string str = "123456789abcdefghlijklmnopqrst";	// 1
    	return 0;					        // 2
    }
    

    在执行进入步骤1时,查看str对象的起始地址为0x0017FA88,可以单步调试std::string源码进入_Myt& assign(const _Elem *_Ptr, size_type _Count)成员函数中,当执行

    this->_Getal().construct(&this->_Bx._Ptr, _Ptr);

    完成后,可以看到str对象_Bx联合体所在地址0x0017FA8C中保存了新申请的内存空间地址0x00610100,如下内存图3:

    图3 申请内存空间

    继续单步调试,会执行到

    _Traits::copy(this->_Myptr(), _Ptr, _Count);

    此时在value_type *_Myptr()的调用中

    this->_BUF_SIZE <= this->_Myres

    条件成立,会返回联合体_Bx的_Ptr内容,即新申请的地址;

    那么后续会调用void *memcpy(void *dest, const void *src, size_t n);

    将字符串拷贝到_Bx联合体的_Ptr指向的地址空间中存储,这里改地址为0x00610100,查看该地址的内存信息,如下图4,该地址起始的30个字节中保存了与代码中初始化一致的字符串。

    图4 拷贝字符串至申请的地址空间

    当程序执行完步骤1时,可以查看如下内存图5,str对象当前字符串大小为30字节,可用有效的空间为31字节。

    图5 更新标识空间大小变量

    3._Myproxy指针申请内存

    在std::string的简介中了_Myproxy的指针对象,该对象包含了const _Container_base12 *_Mycont与_Iterator_base12 *_Myfirstiter两个指针对象,我们知道在32位机下指针本身是占4字节内存的,那么给_Myproxy分配内存按理说就是8字节大小。

    通过分析源码,在std::string对象申明时,会先调用其基类class _String_alloc构造函数,在其深度调用过程中会调用class _String_alloc的void _Alloc_proxy()成员函数

        void _Alloc_proxy()

               {     // construct proxy from _Alval

               typename _Alloc::template rebind<_Container_proxy>::other

                      _Alproxy;

               this->_Myproxy = _Alproxy.allocate(1);

               _Alproxy.construct(this->_Myproxy, _Container_proxy());

               this->_Myproxy->_Mycont = this;

               }

    在this->_Myproxy = _Alproxy.allocate(1)的深度调用中会调用_Ty *_Allocate(size_t _Count, _Ty *)该函数去执行

    _Ptr = ::operator new(_Count * sizeof (_Ty))

    这里_Count为_Alproxy.allocate(1)传进的参数1,_Ty为std:: _Container_proxy类型,所以分配的内存是8字节大小。

    可以使用std::string存储字符串机制中的代码验证,当std::string对象初始化调用void _Alloc_proxy()函数执行完this->_Myproxy = _Alproxy.allocate(1)时,可以查看内存图6,0x0018FDA4为str对象的起始地址,新申请到的_Myproxy地址为0x003400B8。

    图6 _Myproxy申请地址空间

    当调试代码执行this->_Myproxy->_Mycont = this时,查看0x003400B8的内存信息如下图7所示,可以看到_Myproxy对象的_Mycont指向的就是str对象地址0x0018FDA4,_Myfirstier仍然为NULL。

    图7 _Myproxy内存信息

    在str对象完成初始化和赋值后,对比上面初始化str对象时内存信息与局部变量信息,如下图8。

    图8 _Myproxy局部变量地址信息

    4.std::string对象析构

    C++析构函数是先调用子类的析构函数,再调用基类的析构函数。

    1)先调用~basic_string()

    ~basic_string() _NOEXCEPT

                  {     // destroy the string

                  _Tidy(true);

                  }

    在~basic_string()函数中再调用

    void _Tidy(bool _Built = false,

                  size_type _Newsize = 0)

                  {     // initialize buffer, deallocating any storage

                  if (!_Built)

                         ;

                  else if (this->_BUF_SIZE <= this->_Myres)

                         {     // copy any leftovers to small buffer and deallocate

                         pointer _Ptr = this->_Bx._Ptr;

                         this->_Getal().destroy(&this->_Bx._Ptr);

                         if (0 < _Newsize)

                                _Traits::copy(this->_Bx._Buf,

                                       _STD addressof(*_Ptr), _Newsize);

                         this->_Getal().deallocate(_Ptr, this->_Myres + 1);

                         }

                  this->_Myres = this->_BUF_SIZE - 1;

                  _Eos(_Newsize);

                  }

           可以看到,该函数中通过this->_BUF_SIZE <= this->_Myres条件去判断是否需要释放this->_Bx._Ptr的内存。

    2)接着调用~_String_alloc()

    ~_String_alloc() _NOEXCEPT

                  {     // destroy the object

                  _Free_proxy();

                  }

    在该函数中在调用

        void _Free_proxy()

               {     // destroy proxy

               typename _Alloc::template rebind<_Container_proxy>::other

                      _Alproxy;

               this->_Orphan_all();

               _Alproxy.destroy(this->_Myproxy);

               _Alproxy.deallocate(this->_Myproxy, 1);

               this->_Myproxy = 0;

               }

    可以看到该函数完成对_Myproxy内存的释放。

     

    展开全文
  • C++11的std::mem_fn源码解析

    千次阅读 多人点赞 2021-05-19 21:55:47
    C++11的std::mem_fn源码解析1、源码准备2、通过一个简单的例子来了解std::mem_fn的作用3、std::mem_fn源码解析3.1、std::mem_fn解析3.2、std::_Mem_fn解析3.3、在代码中正确使用std::_Mem_fn4、总结 1、源码准备 ...

    1、源码准备

    本文是基于gcc-4.9.0的源代码进行分析,std::mem_fn是C++11才加入标准的,所以低版本的gcc源码是没有std::mem_fn的,建议选择4.9.0或更新的版本去学习,不同版本的gcc源码差异应该不小,但是原理和设计思想的一样的,下面给出源码下载地址
    http://ftp.gnu.org/gnu/gcc

    2、通过一个简单的例子来了解std::mem_fn的作用

    算法是C++标准库中非常重要的组成部分,C++通过算法+容器的方式将数据结构和算法进行了分离,这样可以使用户编写代码的时候获得最大限度的灵活性。假设我们有如下类:

    class Age
    {
    public:
        Age(int v)
            :m_age(v)
        {
        }
    
        bool compare(const Age& t) const
        {
            return m_age < t.m_age;
        }
    
        int m_age;
    };
    

    我们可以非常方便地使用vector来保存Age对象,如下:

    std::vector<Age> ages{1, 7, 19, 27, 39, 16, 13, 18};
    

    然后非常方便的利用排序算法进行排序

    std::sort(ages.begin(), ages.end(), compare);
    

    代码中的compare是额外定义的一个比较函数,通过这个函数来选择比较的对象并决定比较的结果

    bool compare(const Age& t1, const Age& t2)
    {
        return t1.compare(t2);
    }
    

    严格来讲,算法中要求的并不是函数,而是一个可调用对象。C++中的可调用对象包括函数、函数对象、Lambda表达式、参数绑定等等,它们都可以作为算法的传入参数,但是如果我们按如下来传入参数的话,则会在编译过程中出现错误

    std::sort(ages.begin(), ages.end(), &Age::compare);
    

    因为&Age::compare是类成员函数,并非一个可调用对象,如果我们要将它作为比较的参数传递进去的话,就得用std::mem_fn修饰它,如下所示

    std::sort(ages.begin(), ages.end(), std::mem_fn(&Age::compare));
    

    从上面的例子可以看到,std::mem_fn的作用就是将类的成员函数转换为一个可调用对象,那么问题来了,std::mem_fn是如何实现这种功能的呢?下面让我们通过分析源码,来揭开std::mem_fn的神秘面纱。

    3、std::mem_fn源码解析

    3.1、std::mem_fn解析

    std::mem_fn位于libstdc++-v3\include\std\functional

    template<typename _Tp, typename _Class>
    inline _Mem_fn<_Tp _Class::*> mem_fn(_Tp _Class::* __pm) noexcept
    {
    	return _Mem_fn<_Tp _Class::*>(__pm);
    }
    

    从代码中可知std::mem_fn是一个模板函数,传入参数为指向_Class类里面的某个成员函数的指针,其返回值为_Tp,而该模板函数返回的值为_Mem_fn<_Tp _Class::*>,接下来看一下_Mem_fn的实现

    3.2、std::_Mem_fn解析

    std::_Mem_fn位于libstdc++-v3\include\std\functional

    template<typename _Res, typename _Class, typename... _ArgTypes>
    class _Mem_fn<_Res (_Class::*)(_ArgTypes...)> : public _Maybe_unary_or_binary_function<_Res, _Class*, _ArgTypes...>
    {
        typedef _Res (_Class::*_Functor)(_ArgTypes...);
    
        template<typename _Tp, typename... _Args>
        _Res _M_call(_Tp&& __object, const volatile _Class *, _Args&&... __args) const
        {
            return (std::forward<_Tp>(__object).*__pmf)(std::forward<_Args>(__args)...);
        }
    
        template<typename _Tp, typename... _Args>
        _Res _M_call(_Tp&& __ptr, const volatile void *, _Args&&... __args) const
        {
            return ((*__ptr).*__pmf)(std::forward<_Args>(__args)...);
        }
    
        template<typename... _Args>
        using _RequireValidArgs = _Require<_AllConvertible<_Pack<_Args...>, _Pack<_ArgTypes...>>>;
    
        template<typename _Tp, typename... _Args>
        using _RequireValidArgs2 = _Require<_NotSame<_Class, _Tp>, _NotSame<_Class*, _Tp>, _AllConvertible<_Pack<_Args...>, _Pack<_ArgTypes...>>>;
    
        template<typename _Tp, typename... _Args>
        using _RequireValidArgs3 = _Require<is_base_of<_Class, _Tp>, _AllConvertible<_Pack<_Args...>, _Pack<_ArgTypes...>>>;
    
    public:
        typedef _Res result_type;
    
        explicit _Mem_fn(_Functor __pmf) : __pmf(__pmf) {}
    
        template<typename... _Args, typename _Req = _RequireValidArgs<_Args...>>
        _Res operator()(_Class& __object, _Args&&... __args) const
        {
            return (__object.*__pmf)(std::forward<_Args>(__args)...);
        }
    
        template<typename... _Args, typename _Req = _RequireValidArgs<_Args...>>
        _Res operator()(_Class&& __object, _Args&&... __args) const
        {
            return (std::move(__object).*__pmf)(std::forward<_Args>(__args)...);
        }
    
        template<typename... _Args, typename _Req = _RequireValidArgs<_Args...>>
        _Res operator()(_Class* __object, _Args&&... __args) const
        {
            return (__object->*__pmf)(std::forward<_Args>(__args)...);
        }
    
        template<typename _Tp, typename... _Args, typename _Req = _RequireValidArgs2<_Tp, _Args...>>
        _Res operator()(_Tp&& __object, _Args&&... __args) const
        {
            return _M_call(std::forward<_Tp>(__object), &__object,
            std::forward<_Args>(__args)...);
        }
    
        template<typename _Tp, typename... _Args,
        typename _Req = _RequireValidArgs3<_Tp, _Args...>>
        _Res operator()(reference_wrapper<_Tp> __ref, _Args&&... __args) const
        {
            return operator()(__ref.get(), std::forward<_Args>(__args)...);
        }
    
    private:
        _Functor __pmf;
    };
    

    从源代码中可以看出以下几点信息:

    1. 该类继承于_Maybe_unary_or_binary_function,由于_Maybe_unary_or_binary_function和本文分析的内容没有太大关联,大家可以自行百度查询其用法,这里就不多作介绍了
    2. 类中有一个成员__pmf,其类型是指向上一节传入mem_fn的那个类成员函数的指针,由构造函数初始化
    3. 接下来重点看一下类中六个重载的()运算符函数,里面的操作大同小异,基本都是通过__pmf对应的类的对象(多种形式)来调用__pmf成员函数的:
    • 第一个函数_Res operator()(_Class& __object, _Args&&… __args):可以看到,其比原始的类成员函数多要求了一个传入参数,也就是__object,类型是一个类对象的引用,从函数的实现中可以看到原理就是通过这个类对象来直接调用先前那个类成员函数的(没有这个类对象就调用不成立了,因为类成员函数是无法直接调用的,这也是std::mem_fn存在的意义)
    • 第二个函数_Res operator()(_Class&& __object, _Args&&… __args):可以看到该方法第一个传入参数是一个右值引用对象,里面的实现就是通过std::move将对象进行转移而已,其它处理与前面是完全一样的
    • 第三个函数_Res operator()(_Class* __object, _Args&&… __args):可以看到该方法传入了一个对象指针,其它处理与前面是完全一样的
    • 第五个函数_Res operator()(reference_wrapper<_Tp> __ref, _Args&&… __args):可以看到该方法传入了一个被std::reference_wrapper包装的引用,流程和前面的基本一致,比较简单,这里就不多作分析了
      (关于std::reference_wrapper的问题大家可以看一下这篇文章《C++11的std::ref、std::cref源码解析》,里面有通过源码分析对std::reference_wrapper作出了详细的介绍,这里就不重复说明了)
    • 第四个函数_Res operator()(_Tp&& __object, _Args&&… __args):这个就比较复杂了,这个函数是为了处理传入参数是智能指针或者派生类对象的一个情况的。可以看到函数里调用了_M_call方法,第二个参数看似可有可无,其实是为了用于给_M_call区分传入参数类型是一个智能指针还是一个派生类对象的
    1. _M_call实现如下,可以看到,第一个重载的形式是处理派生类对象的,第二个重载的形式是处理智能指针的,代码比较简单,这里就不多作分析了,大家可以自行看一遍就明白了
    template<typename _Tp, typename... _Args>
    _Res _M_call(_Tp&& __object, const volatile _Class *, _Args&&... __args) const
    {
        return (std::forward<_Tp>(__object).*__pmf)(std::forward<_Args>(__args)...);
    }
    
    template<typename _Tp, typename... _Args>
    _Res _M_call(_Tp&& __ptr, const volatile void *, _Args&&... __args) const
    {
        return ((*__ptr).*__pmf)(std::forward<_Args>(__args)...);
    }
    

    3.3、在代码中正确使用std::_Mem_fn

    示例代码如下,从上面的一大段分析可以知道,我们传入的ages[2]就是之前一直分析的那个用于调用类成员函数的那个传入对象,而ages[3]就是bool Age::compare(const Age& t)所需要的正常的传入参数了,也就是上面的可变参数里面的值。至此std::mem_fn源码也就分析完毕了

    #include <functional>
    #include <iostream>
    #include <algorithm>
    #include <vector>
    
    class Age
    {
    public:
        Age(int v)
            :m_age(v)
        {
        }
    
        bool compare(const Age& t) const
        {
            return m_age < t.m_age;
        }
    
        int m_age;
    };
    
    bool compare(const Age& t1, const Age& t2)
    {
        return t1.compare(t2);
    }
    
    int main(int argc, char* argv[])
    {
        std::vector<Age> ages{1, 7, 19, 27, 39, 16, 13, 18};
        bool ret = std::mem_fn(&Age::compare)(ages[2], ages[3]);
        //std::sort(ages.begin(), ages.end(), std::mem_fn(&Age::compare));
    
        return 0;
    }
    

    4、总结

    std::mem_fn在函数式编程中的作用是非常大的,我们可以使用std::mem_fn生成指向类成员函数的指针的包装对象,该对象可以存储,复制和调用指向类成员函数的指针。而我们实际使用的是std::mem_fn的返回值std::_Mem_fn这个类,而我们在调用std::_Mem_fn中重载的()方法时,可以使用类对象、派生类对象、对象引用(包括std::reference_wrapper)、对象的右值引用、指向对象的指针(包括智能指针)来作为第一个参数传递进去。

    最后,如果大家觉得本文写得好的话麻烦点赞收藏关注一下谢谢,也可以关注该专栏,以后会有更多优质文章输出的。

    展开全文
  • 二、SymbolTable和StringTable 三、ConstantPool 四、SystemDictionary 五、ClassLoader 六、ClassLoader JNI接口实现 1、findLoadedClass0方法 2、findBootstrapClass方法 3、resolveClass0 4、defineClass...

    目录

    一、Symbol

    二、SymbolTable和StringTable

    三、ConstantPool

    四、SystemDictionary

    五、ClassLoader

    六、ClassLoader JNI接口实现

    1、findLoadedClass0方法

     2、findBootstrapClass方法

    3、resolveClass0

    4、 defineClass0、defineClass1、defineClass2

    七、类链接

    八、类初始化


           在《Hotspot 类加载器Java源码解析》一文中讲到从JVM已经加载的类中查找目标类由本地方法findLoadedClass0实现,通过启动类加载器加载类由本地方法findLoadedClass0实现,将已经加载完成的class解析并完成链接由本地方法findLoadedClass0实现,将读取class文件的字节数组转换成Class由defineClass0,defineClass1,defineClass2等方法完成,这四个本地方法的底层逻辑是什么样的?神秘的启动类加载器是如何加载rt.jar的?符号引用的解析是如何完成的?类的链接和初始化是怎样实现的?

    一、Symbol

          Symbol类的定义位于oops/symbol.hpp中,Symbol表示一个规范化的字符串形式的描述符,如方法Object m(int i, double d, Thread t) {...}对应的方法描述符就是(IDLjava/lang/Thread;)Ljava/lang/Object,参考《Hotspot class文件和字节码解析》。所有的Symbol实例通过保存在全局的SymbolTable中,SymbolTable即符号表,基于此实现符号引用计数功能。当有一个新的指针指向该Symbol实例,则引用计数加1,当该指针销毁时需要将引用计数减1,当一个Symbol的引用计数为0,垃圾回收器就会从SymbolTable中删除该Symbol并回收内存。

    其类继承关系如下图:

    SymbolBase定义了三个Symbol的基础属性:

    • _refcount:支持原子操作的short变量,表示该Symbol的引用计数
    • _length:UTF-8字符串的长度
    • _identity_hash:hash唯一标识码

    Symbol增加一个字节数组的属性_body,用于存储描述符对应的字符串,定义了可以操作该属性的byte_at_put(int index, int value)方法,打印具体字符串内容的as_C_string(),as_utf8()方法,以及引用计数相关的refcount(),increment_refcount(),decrement_refcount()方法。

    二、SymbolTable和StringTable

         SymbolTable和StringTable类的定义位于classfile/symbolTable.hpp中,对应于C/C++编译过程的符号表,用于保存管理所有的Symbol实例,StringTable就是Java特有的字符串常量池。其类继承关系如下:

    即SymbolTable和StringTable实际是一个支持自动扩容的HashMap。 

    SymbolTable定义了如下五个静态属性:

    • _the_table:SymbolTable指针,即全局实际保存Symbol实例的地方
    • _needs_rehashing:bool变量,是否需要重新hash
    • _symbols_removed:int变量,已经被移除的Symbol的数量
    • _symbols_counted:int变量,当前的Symbol的属性
    • _arena:Arena类指针,表示从未被加载过的描述符

    SymbolTable定义的方法都是静态方法,主要有两大类:

    • Symbol创建,释放和查找相关的,如allocate_symbol,new_symbols,new_symbol,release,lookup,probe等方法
    • HashTable自身创建复制相关的,如create_table,copy_buckets,copy_table,rehash_table等。

     StringTable定义了三个静态属性,

    • _the_table:StringTable指针,全局实际保存字符串的地方
    • _needs_rehashing:bool变量,是否需要重新hash
    • _parallel_claimed_idx:volatile int变量,并发标记时使用

    StringTable也重写了HashTable自身创建复制相关的方法,除此之外增加了用于往常量池添加字符串的intern方法,查找字符串的lookup方法。

    以根据描述符字符串查找对应Symbol实例的lookup方法为例说明其逻辑:

    Symbol* SymbolTable::lookup(const char* name, int len, TRAPS) {
      //计算hash值
      unsigned int hashValue = hash_symbol(name, len);
      //计算hash槽的位置
      int index = the_table()->hash_to_index(hashValue);
      //去指定位置的hash槽中查找是否存在目标Symbol
      Symbol* s = the_table()->lookup(index, name, len, hashValue);
      //如果存在则返回
      if (s != NULL) return s;
      //如果不存在则需要获取锁SymbolTable_lock,并创建一个新的
      MutexLocker ml(SymbolTable_lock, THREAD);
      //会调用allocate_symbol在内存中创建一个新的Symbol,然后以此构建一个新的HashtableEntry实例,将其添加到HashTable中
      return the_table()->basic_add(index, (u1*)name, len, hashValue, true, CHECK_NULL);
    }

      即符号表中的符号都只是原始字符串形式的描述符,并未涉及符号解析以及解析结果的保存。

    三、ConstantPool

         ConstantPool类的定义位于oops/constantPool.hpp文件中,用于表示class文件中的常量池,每个Klass都有对应的ConstantPool,两者是一一对应的。常量池的数据大部分是在class文件解析的时候写入的,可以安全访问;但是CONSTANT_Class_info类型的常量数据是在这个类被解析时修改,这时只能通过解析状态判断这条数据是否修改完成。其类继承关系如下:

    常量池的每项数据都通过类CPSlot表示,其定义跟ConstantPool类位于同一个文件中,只有一个属性,解析结果Klass或者Symbol的地址,如果未解析则地址是0,可以将该地址转换成Klass或者Symbol类的指针,定义了对应的转换方法(get_symbol和get_klass方法)和判断该数据项是否已经解析的方法(is_resolved和is_unresolved方法)。

    ConstantPool定义如下关键属性:

    • _tags:单字节数组指针,描述常量池所有数据的类型的tag数组,每个tag用一个单字节表示
    • _cache:ConstantPoolCache类指针,保存解释器运行时用到的动态调用相关信息的缓存
    • _pool_holder:InstanceKlass指针,当前常量池所属的Klass实例
    • _operands:两字节的数组指针,为大小可变的常量池数据项使用,通常为空
    • _resolved_references:jobject类型,实际是_jobject指针的别名,_jobject等同于C++层面的Java Object对象,表示已经解析的对象数组
    • _reference_map:两字节的数组指针,表示已经解析的对象的索引到原始的常量池的索引的映射关系

    ConstantPool定义了读取和解析常量池数据的诸多方法,大致有以下几类:

    • 属性读写的方法,如tags,set_tags,flags,set_flags,pool_holder,set_pool_holder,cache,set_cache等方法
    • 读取基地址指定偏移位置的值的方法,如obj_at_addr_raw,long_at_addr,double_at_addr等方法
    • 向常量池指定位置写入数据的方法,解析class文件时调用该类方法,如klass_at_put,unresolved_klass_at_put,method_handle_index_at_put,invoke_dynamic_at_put,int_at_put,field_at_put,name_and_type_at_put等方法
    • 从常量池指定位置读取数据的方法,如klass_at,klass_name_at,resolved_klass_at,long_at,symbol_at,name_and_type_at,method_handle_name_ref_at等方法
    • 解析常量池符号引用的方法,如resolve_constant_at,resolve_bootstrap_specifier_at,resolve_constant_at_impl,resolve_string_constants_impl,resolve_bootstrap_specifier_at_impl等方法
    • 从class文件读取常量池的字节流时校验常量池是否符合规范的方法,如verify_on

    至此答案已经明确,符号引用的解析,解析结果的缓存都由ConstantPool完成,以klass_at_impl(constantPoolHandle this_oop, int which, TRAPS)方法的实现为例说明,如下:

    Klass* ConstantPool::klass_at_impl(constantPoolHandle this_oop, int which, TRAPS) {
      //获取指定位置的数据项,判断其是否已解析
      CPSlot entry = this_oop->slot_at(which);
      if (entry.is_resolved()) {
        //如果已解析但是不是Klass类型则抛出异常
        assert(entry.get_klass()->is_klass(), "must be");
        //返回解析结果
        return entry.get_klass();
      }
    
      //判断调用方线程是否是Java线程,不能是本地的C++线程
      assert(THREAD->is_Java_thread(), "must be a Java thread");
      bool do_resolve = false;
      bool in_error = false;
    
      //用于创建目标类Class即InstanceMirrorKlass实例的Handle
      Handle mirror_handle;
    
      Symbol* name = NULL;
      Handle       loader;
      //获取常量池的锁
      {  MonitorLockerEx ml(this_oop->lock());
        //进一步校验,如果未解析
        if (this_oop->tag_at(which).is_unresolved_klass()) {
           //如果是因为解析异常导致未解析,则将in_error置为true
          if (this_oop->tag_at(which).is_unresolved_klass_in_error()) {
            in_error = true;
          } else {
             //准备开始解析
            do_resolve = true;
             //获取完整类名
            name   = this_oop->unresolved_klass_at(which);
            //根据当前常量池所属的Klass实例获取加载该Klass实例的类加载器
            loader = Handle(THREAD, this_oop->pool_holder()->class_loader());
          }
        }
      } // unlocking constantPool
    
    
      //解析失败抛出异常
      if (in_error) {
        Symbol* error = SystemDictionary::find_resolution_error(this_oop, which);
        guarantee(error != (Symbol*)NULL, "tag mismatch with resolution error table");
        ResourceMark rm;
        // exception text will be the class name
        const char* className = this_oop->unresolved_klass_at(which)->as_C_string();
        THROW_MSG_0(error, className);
      }
    
      if (do_resolve) {
        // 执行resolve_or_fail时常量池必须处于非锁定状态
        oop protection_domain = this_oop->pool_holder()->protection_domain();
        Handle h_prot (THREAD, protection_domain);
        //通过SystemDictionary调用类加载器Oop解析目标类
        Klass* k_oop = SystemDictionary::resolve_or_fail(name, loader, h_prot, true, THREAD);
        KlassHandle k;
        if (!HAS_PENDING_EXCEPTION) {
    
          //如果解析成功
          k = KlassHandle(THREAD, k_oop);
          //初始化mirror_handle
          mirror_handle = Handle(THREAD, k_oop->java_mirror());
          //校验访问权限
          verify_constant_pool_resolve(this_oop, k, THREAD);
        }
    
        //解析失败,必须记录具体的失败原因,这样后面的解析都会以同样的原因失败
        if (HAS_PENDING_EXCEPTION) {
          ResourceMark rm;
          Symbol* error = PENDING_EXCEPTION->klass()->name();
    
          bool throw_orig_error = false;
          {
            MonitorLockerEx ml(this_oop->lock());
    
            //校验是否因为并发导致解析失败,即有一个线程解析成功
            if (this_oop->tag_at(which).is_klass()) {
              CLEAR_PENDING_EXCEPTION;
              entry = this_oop->resolved_klass_at(which);
              //返回解析成功的结果
              return entry.get_klass();
            }
    
            if (!PENDING_EXCEPTION->
                  is_a(SystemDictionary::LinkageError_klass())) {
              //如果不是链接类错误则直接抛出异常不用记录,如StackOverflow
            }
            else if (!this_oop->tag_at(which).is_unresolved_klass_in_error()) {
              //记录解析失败异常
              SystemDictionary::add_resolution_error(this_oop, which, error);
              this_oop->tag_at_put(which, JVM_CONSTANT_UnresolvedClassInError);
            } else {
              //如果是其他线程已经记录了异常原因则获取具体原因然后抛出异常
              error = SystemDictionary::find_resolution_error(this_oop, which);
              assert(error != NULL, "checking");
              throw_orig_error = true;
            }
          } // unlocked
          
          //抛出异常
          if (throw_orig_error) {
            CLEAR_PENDING_EXCEPTION;
            ResourceMark rm;
            const char* className = this_oop->unresolved_klass_at(which)->as_C_string();
            THROW_MSG_0(error, className);
          }
    
          return 0;
        }
        //如果需要根据类加载并且当前类不是数组类型的
        if (TraceClassResolution && !k()->oop_is_array()) {
          ResourceMark rm;
          int line_number = -1;
          const char * source_file = NULL;
          if (JavaThread::current()->has_last_Java_frame()) {
            // try to identify the method which called this function.
            vframeStream vfst(JavaThread::current());
            if (!vfst.at_end()) {
              //获取触发当前解析的代码行号和源代码文件名
              line_number = vfst.method()->line_number_from_bci(vfst.bci());
              Symbol* s = vfst.method()->method_holder()->source_file_name();
              if (s != NULL) {
                source_file = s->as_C_string();
              }
            }
          }
          //如果待加载类不是当前常量池所属的类
          if (k() != this_oop->pool_holder()) {
            if (source_file != NULL) {
              tty->print("RESOLVE %s %s %s:%d\n",
                         this_oop->pool_holder()->external_name(),
                         InstanceKlass::cast(k())->external_name(), source_file, line_number);
            } else {
              tty->print("RESOLVE %s %s\n",
                         this_oop->pool_holder()->external_name(),
                         InstanceKlass::cast(k())->external_name());
            }
          }
          return k();
        } else {
          MonitorLockerEx ml(this_oop->lock());
          //获取锁进一步校验常量池该项数据是否未解析
          do_resolve = this_oop->tag_at(which).is_unresolved_klass();
          if (do_resolve) {
            //如果未解析则将接解析结果存入常量池
            this_oop->klass_at_put(which, k());
          }
        }
      }
    
      entry = this_oop->resolved_klass_at(which);
      assert(entry.is_resolved() && entry.get_klass()->is_klass(), "must be resolved at this point");
      return entry.get_klass();
    }
    

    四、SystemDictionary

          SystemDictionary类的定义在classfile/systemDictionary.hpp中,是一个系统字典类,用于保存所有已经加载完成的类,通过一个支持自动扩容的HashMap保存,key是表示类名Symbol指针和对应的类加载器oop指针,value是对应的Klass指针,当一个新的类加载完成后就会在SystemDictionary中添加一个新的键值对,其类继承关系如下:

         SystemDictionary定义的属性和方法基本都是静态的,其关键属性如下:

    • _sdgeneration:int变量,保存已加载类的HashMap的容量
    • _dictionary:Dictionary类指针,实际保存已加载类的HashMap
    • _placeholders:PlaceholderTable类指针,当类加载的过程中临时存储键值对的地方,底层数据结构同Dictionary类
    • _shared_dictionary:Dictionary类指针,共享架构下用于保存已加载类的HashMap
    • _number_of_modifications: int变量,发生修改的次数,类加载或者删除都会增加该计数器
    • _system_loader_lock_obj:oop指针,系统类加载器的对象锁
    • _loader_constraints:LoaderConstraintTable类指针,保存类加载器加载约束的HashTable
    • _resolution_errors:ResolutionErrorTable类指针,保存类解析错误的HashTable
    • _invoke_method_table:SymbolPropertyTable类指针,保存MethodHandle调用的解析结果
    • _java_system_loader:oop指针,系统类加载器的引用

        其定义 的方法主要有以下几类:

    •    属性操作的相关方法,如check_constraints,add_placeholder,add_klass,dictionary等
    •    根据类加载器和类名加载类的方法,如resolve_or_fail,resolve_or_null,resolve_super_or_fail
    •    根据class文件流,类加载器和类名加载类的方法,如parse_stream,resolve_from_stream
    •    根据类名和类加载器从已经加载的类中查找目标类,如find,find_instance_or_array_klass
    •    根据符号引用解析MethodHandle调用的方法,如find_method_handle_invoker,find_method_handle_type

       综上可知SystemDictionary相当于类加载的一个统一入口,同时提供查找已加载的类和加载新的类的服务,实现逻辑比较复杂,有兴趣的可以参考源码。

    五、ClassLoader

         ClassLoader类的定义在classfile/classLoader.hpp中,ClassLoader就是传说中的用于加载Java核心类文件如rt.jar的启动类加载器的实现,其类继承关系如下:

    ClassLoader定义的属性大都是用于统计类加载性能的计数器,如_perf_class_parse_time,PerfCounter类指针,用于统计类解析的累计耗时,除此之外有以下几个静态属性:

    • _first_entry:ClassPathEntry类指针,ClassPathEntry用于表示单个classpath路径,所有的ClassPathEntry实例以链表的形式关联起来,_first_entry表示链表的第一个实例
    • _last_entry:ClassPathEntry类指针,表示链表的最后一个实例
    • _num_entries:int变量,表示ClassPathEntry链表中ClassPathEntry实例的个数
    • _package_hash_table:PackageHashtable类指针,用于保存已经加载过的包名

    ClassLoader定义的方法不多,大多是统计类加载性能相关的,除此之外有以下几个方法比较重要:

    • 加载zip文件读取写入等操作的动态链接库,load_zip_library方法
    • ClassPathEntry相关的,如setup_search_path,contains_entry,add_to_list,num_classpath_entries,classpath_entry,create_class_path_entry,update_class_path_entry_list等
    • 初始化的方法,initialize
    • 根据类名加载指定类文件的方法,load_classfile

    其中initialize的调用链如下图:

     其实现逻辑比较简单,关键代码如下:

    void ClassLoader::initialize() {
      assert(_package_hash_table == NULL, "should have been initialized by now.");
      EXCEPTION_MARK;
    
      if (UsePerfData) {
        //如果开始性能检测则初始化各计数器
        NEWPERFTICKCOUNTER(_perf_accumulated_time, SUN_CLS, "time");
        NEWPERFTICKCOUNTER(_perf_class_init_time, SUN_CLS, "classInitTime");
      }
    
      //加载读写zip文件的动态链接库
      load_zip_library();
      //设置加载核心jar包的搜索路径,从系统参数Arguments中获取
      setup_bootstrap_search_path();
      //如果是惰性启动加载,即启动时不加载rt.jar等文件
      if (LazyBootClassLoader) {
        //设置meta_index_path,设置完成后会触发对meta_index_path下文件的解析
        setup_bootstrap_meta_index();
      }
    }

      load_classfile的调用链如下:

    查看SystemDictionary的load_instance_class方法代码可知当类加载器oop为空的时候会调用load_classfile方法,即当其他的Java类加载器无法加载特定类的时候将类加载请求委托给启动类加载器加载,其关键代码如下:

    instanceKlassHandle SystemDictionary::load_instance_class(Symbol* class_name, Handle class_loader, TRAPS) {
      instanceKlassHandle nh = instanceKlassHandle(); // null Handle
      //如果class_loader为空,即Java类加载器无法加载该类了
      if (class_loader.is_null()) {
        instanceKlassHandle k;
        //目标类未加载
        if (k.is_null()) {
          //执行目标类的加载
          k = ClassLoader::load_classfile(class_name, CHECK_(nh));
        }
    
        //如果已经加载则查找加载的类
        if (!k.is_null()) {
          k = find_or_define_instance_class(class_name, class_loader, k, CHECK_(nh));
        }
        return k;
      } else {
        ResourceMark rm(THREAD);
    
        assert(THREAD->is_Java_thread(), "must be a JavaThread");
        JavaThread* jt = (JavaThread*) THREAD;
        
        //构建JNI调用的参数,即类名
        Handle s = java_lang_String::create_from_symbol(class_name, CHECK_(nh));
        Handle string = java_lang_String::externalize_classname(s, CHECK_(nh));
        //调用的结果
        JavaValue result(T_OBJECT);
        //结果的处理器
        KlassHandle spec_klass (THREAD, SystemDictionary::ClassLoader_klass());
        //调用Java的类加载器加载特定类
        if (MustCallLoadClassInternal && has_loadClassInternal()) {
          JavaCalls::call_special(&result,
                                  class_loader,
                                  spec_klass,
                                  vmSymbols::loadClassInternal_name(),
                                  vmSymbols::string_class_signature(),
                                  string,
                                  CHECK_(nh));
        } else {
          JavaCalls::call_virtual(&result,
                                  class_loader,
                                  spec_klass,
                                  vmSymbols::loadClass_name(),
                                  vmSymbols::string_class_signature(),
                                  string,
                                  CHECK_(nh));
        }
        //从加载结果中获取目标类oop
        assert(result.get_type() == T_OBJECT, "just checking");
        oop obj = (oop) result.get_jobject();
    
        //检查访问权限
        if ((obj != NULL) && !(java_lang_Class::is_primitive(obj))) {
          instanceKlassHandle k =
                    instanceKlassHandle(THREAD, java_lang_Class::as_Klass(obj));
          //检查类名是否一致
          if (class_name == k->name()) {
            return k;
          }
        }
        //类加载失败,返回空对象
        return nh;
      }
    }

    load_classfile方法的关键代码如下: 

    instanceKlassHandle ClassLoader::load_classfile(Symbol* h_name, TRAPS) {
      ResourceMark rm(THREAD);
      //获取类名
      const char* class_name = h_name->as_C_string();
      EventMark m("loading class %s", class_name);
      ThreadProfilerMark tpm(ThreadProfilerMark::classLoaderRegion);
    
      stringStream st;
      st.print_raw(h_name->as_utf8());
      st.print_raw(".class");
      //获取文件名
      const char* file_name = st.as_string();
      ClassLoaderExt::Context context(class_name, file_name, THREAD);
    
      //ClassFileStream表示Class文件的字节流
      ClassFileStream* stream = NULL;
      int classpath_index = 0;
      ClassPathEntry* e = NULL;
      instanceKlassHandle h;
      {
        //从第一个ClassPathEntry开始遍历所有的ClassPathEntry
        e = _first_entry;
        while (e != NULL) {
          stream = e->open_stream(file_name, CHECK_NULL);
          //如果检查返回false则返回null,check方法默认返回true
          if (!context.check(stream, classpath_index)) {
            return h; // NULL
          }
          //如果找到目标文件则跳出循环
          if (stream != NULL) {
            break;
          }
          e = e->next();
          ++classpath_index;
        }
      }
      //如果找到了目标class文件
      if (stream != NULL) {
        //构建一个ClassFileParser实例
        ClassFileParser parser(stream);
        //构建一个ClassLoaderData实例
        ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data();
        Handle protection_domain;
        TempNewSymbol parsed_name = NULL;
        //解析并加载class文件,注意此时并未开始链接
        instanceKlassHandle result = parser.parseClassFile(h_name,
                                                           loader_data,
                                                           protection_domain,
                                                           parsed_name,
                                                           context.should_verify(classpath_index),
                                                           THREAD);
        //如果解析异常
        if (HAS_PENDING_EXCEPTION) {
          ResourceMark rm;
          if (DumpSharedSpaces) {
            //打印异常
            tty->print_cr("Preload Error: Failed to load %s", class_name);
          }
          return h;
        }
        //调用ClassLoader的add_package方法,把当前类的包名加入到_package_hash_table中
        h = context.record_result(classpath_index, e, result, THREAD);
      } else {
        //没有找到目标文件
        if (DumpSharedSpaces) {
          tty->print_cr("Preload Warning: Cannot find %s", class_name);
        }
      }
    
      return h;
    }

       再看另一个问题,启动类加载器是什么时候开始加载rt.jar等核心jar包的,是一次性把jar包中所有类都加载进去么?答案是需要的时候,当启动类执行main方法或者JVM自身启动的过程中需要用到rt.jar包中的某个类如java.lang.Thread等就会触发rt.jar的加载,加载特定需要的类。测试用例如下:

    package jvmTest;
    
    import java.lang.management.ManagementFactory;
    import java.lang.management.RuntimeMXBean;
    
    
    public class MainTest3 {
    
        public static void main(String[] args) {
    
            while (true) {
                try {
                    System.out.println(getProcessID());
                    Thread.sleep(600 * 1000);
                } catch (Exception e) {
    
                }
            }
        }
    
        public static final int getProcessID() {
            RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
            System.out.println(runtimeMXBean.getName());
            return Integer.valueOf(runtimeMXBean.getName().split("@")[0])
                    .intValue();
        }
    
    }

       执行main方法,启动HSDB,然后查找DriverManager类,该类是JDBC的驱动管理类,位于rt.jar中,结果如下:

    六、ClassLoader JNI接口实现

          ClassLoader JNI接口的实现源码在jdk/src/share/native/java/lang/ClassLoader.c中

    1、findLoadedClass0方法

          该方法实际调用JVM_FindLoadedClass方法,关键代码如下:

    //JVM_ENTRY是宏定义,用于处理JNI调用的预处理,如获取当前线程的JavaThread指针
    JVM_ENTRY(jclass, JVM_FindLoadedClass(JNIEnv *env, jobject loader, jstring name))
      JVMWrapper("JVM_FindLoadedClass");
      //THREAD表示当前线程
      ResourceMark rm(THREAD);
    
      Handle h_name (THREAD, JNIHandles::resolve_non_null(name));
      //获取类名对应的Handler
      Handle string = java_lang_String::internalize_classname(h_name, CHECK_NULL);
    
      //检查是否为空
      const char* str   = java_lang_String::as_utf8_string(string());
      if (str == NULL) return NULL;
    
      //判断类名是否超长
      const int str_len = (int)strlen(str);
      if (str_len > Symbol::max_length()) {
        return NULL;
      }
      //创建一个临时的Symbol
      TempNewSymbol klass_name = SymbolTable::new_symbol(str, str_len, CHECK_NULL);
    
      //获取类加载器对应的Handler
      Handle h_loader(THREAD, JNIHandles::resolve(loader));
      //查找目标类是否存在
      Klass* k = SystemDictionary::find_instance_or_array_klass(klass_name,
                                                                  h_loader,
                                                                  Handle(),
                                                              CHECK_NULL);
      //将Klass转换成Java中的Class                                                         
      return (k == NULL) ? NULL :
                (jclass) JNIHandles::make_local(env, k->java_mirror());
    JVM_END

     2、findBootstrapClass方法

       该方法时委托启动类加载器加载特定类,该方法在检查类名合法后调用JVM_FindClassFromBootLoader完成加载,关键代码如下:

    JVM_ENTRY(jclass, JVM_FindClassFromBootLoader(JNIEnv* env,
                                                  const char* name))
      JVMWrapper2("JVM_FindClassFromBootLoader %s", name);
    
      //检查类名是否合法
      if (name == NULL || (int)strlen(name) > Symbol::max_length()) {
        return NULL;
      }
    
      //调用SystemDictionary解析目标类,如果未找到返回null
      TempNewSymbol h_name = SymbolTable::new_symbol(name, CHECK_NULL);
      Klass* k = SystemDictionary::resolve_or_null(h_name, CHECK_NULL);
      if (k == NULL) {
        return NULL;
      }
      //将Klass转换成java中Class
      return (jclass) JNIHandles::make_local(env, k->java_mirror());
    JVM_END

    3、resolveClass0

         该方法的本意是解析类,该方法实际并不会调用,只是兼容JDK1.1而保留了下来。该方法会调用JVM_ResolveClass完成解析,如下,OpenJDK只是提供了一个空实现。

    4、 defineClass0、defineClass1、defineClass2

         defineClass0实际调用defineClass1的实现,defineClass1和defineClass2的区别就在于保存字节数据的数组是位于堆内存的普通数组还是位于元空间堆外内存的java.nio.ByteBuffer,两者的处理逻辑基本一致,就是将对应数组的数据拷贝到C++的字节数组中,然后调用JVM_DefineClassWithSource方法,最终调用jvm_define_class_common方法,该方法的关键代码如下:

    static jclass jvm_define_class_common(JNIEnv *env, const char *name,
                                          jobject loader, const jbyte *buf,
                                          jsize len, jobject pd, const char *source,
                                          jboolean verify, TRAPS) {
      if (source == NULL)  source = "__JVM_DefineClass__";
    
      assert(THREAD->is_Java_thread(), "must be a JavaThread");
      JavaThread* jt = (JavaThread*) THREAD;
    
      // Since exceptions can be thrown, class initialization can take place
      // if name is NULL no check for class name in .class stream has to be made.
      TempNewSymbol class_name = NULL;
      if (name != NULL) {
        const int str_len = (int)strlen(name);
        //检查类名的长度是否合法
        if (str_len > Symbol::max_length()) {
          THROW_MSG_0(vmSymbols::java_lang_NoClassDefFoundError(), name);
        }
        //创建一个新的Symbol实例
        class_name = SymbolTable::new_symbol(name, str_len, CHECK_NULL);
      }
    
      ResourceMark rm(THREAD);
      //根据字节数组和文件名构建ClassFileStream实例
      ClassFileStream st((u1*) buf, len, (char *)source);
      //构建Java类加载器实例对应的Hanlder
      Handle class_loader (THREAD, JNIHandles::resolve(loader));
      //构建ProtectionDomain对应的Hander
      Handle protection_domain (THREAD, JNIHandles::resolve(pd));
      //完成字节数组的解析并创建一个Klass实例
      Klass* k = SystemDictionary::resolve_from_stream(class_name, class_loader,
                                                         protection_domain, &st,
                                                         verify != 0,
                                                         CHECK_NULL);
      //将Klass实例转换成Java 的Class
      return (jclass) JNIHandles::make_local(env, k->java_mirror());
    }
    

    七、类链接

          链接包含验证,准备和解析,其中符号引用的解析有ConstantPool完成,验证则是由Verifier类完成,该类的定义在classfile/verifier.hpp中,该类比较简单,核心方法就一个静态方法verify,其调用关系如下:

       从调用关系可知,类链接的入口就是InstanceKlass::link_class_impl方法了,其关键代码如下:

    bool InstanceKlass::link_class_impl(
        instanceKlassHandle this_oop, bool throw_verifyerror, TRAPS) {
      //如果类状态异常
      if (this_oop->is_in_error_state()) {
        ResourceMark rm(THREAD);
        THROW_MSG_(vmSymbols::java_lang_NoClassDefFoundError(),
                   this_oop->external_name(), false);
      }
      //如果类已链接则返回
      if (this_oop->is_linked()) {
        return true;
      }
    
     
      assert(THREAD->is_Java_thread(), "non-JavaThread in link_class_impl");
      JavaThread* jt = (JavaThread*)THREAD;
    
      instanceKlassHandle super(THREAD, this_oop->super());
      if (super.not_null()) {
        //如果父类是一个接口则抛出异常
        if (super->is_interface()) {  
          ResourceMark rm(THREAD);
          Exceptions::fthrow(
            THREAD_AND_LOCATION,
            vmSymbols::java_lang_IncompatibleClassChangeError(),
            "class %s has interface %s as super class",
            this_oop->external_name(),
            super->external_name()
          );
          return false;
        }
        //完成父类的链接
        link_class_impl(super, throw_verifyerror, CHECK_false);
      }
    
      //完成当前类实现的所有接口的链接
      Array<Klass*>* interfaces = this_oop->local_interfaces();
      int num_interfaces = interfaces->length();
      for (int index = 0; index < num_interfaces; index++) {
        HandleMark hm(THREAD);
        instanceKlassHandle ih(THREAD, interfaces->at(index));
        link_class_impl(ih, throw_verifyerror, CHECK_false);
      }
    
      //某些情况下链接父类的时候会把子类链接了,此时做检查是否已链接
      if (this_oop->is_linked()) {
        return true;
      }
    
      
      {
        //初始化对象锁
        oop init_lock = this_oop->init_lock();
        ObjectLocker ol(init_lock, THREAD, init_lock != NULL);
        // rewritten will have been set if loader constraint error found
        // on an earlier link attempt
        // don't verify or rewrite if already rewritten
    
        if (!this_oop->is_linked()) {
          if (!this_oop->is_rewritten()) {
            {
              //完成字节码验证
              bool verify_ok = verify_code(this_oop, throw_verifyerror, THREAD);
              if (!verify_ok) {
                return false;
              }
            }
    
            //在校验是否验证完成
            if (this_oop->is_linked()) {
              return true;
            }
    
            // also sets rewritten
            this_oop->rewrite_class(CHECK_false);
          } else if (this_oop()->is_shared()) {
            ResourceMark rm(THREAD);
            char* message_buffer; // res-allocated by check_verification_dependencies
            Handle loader = this_oop()->class_loader();
            Handle pd     = this_oop()->protection_domain();
            //依赖约束检查
            bool verified = SystemDictionaryShared::check_verification_dependencies(this_oop(),
                            loader, pd, &message_buffer, THREAD);
            if (!verified) {
              THROW_MSG_(vmSymbols::java_lang_VerifyError(), message_buffer, false);
            }
          }
    
          //完成方法链接,即方法的入参和返回值的类型的链接
          this_oop->link_methods(CHECK_false);
          //初始化vtable和itable
          ClassLoaderData * loader_data = this_oop->class_loader_data();
          if (!(this_oop()->is_shared() &&
                loader_data->is_the_null_class_loader_data())) {
            ResourceMark rm(THREAD);
            this_oop->vtable()->initialize_vtable(true, CHECK_false);
            this_oop->itable()->initialize_itable(true, CHECK_false);
          }
          //设置类的状态为链接完成
          this_oop->set_init_state(linked);
          if (JvmtiExport::should_post_class_prepare()) {
            Thread *thread = THREAD;
            assert(thread->is_Java_thread(), "thread->is_Java_thread()");
            //发布JVMTI事件
            JvmtiExport::post_class_prepare((JavaThread *) thread, this_oop());
          }
        }
      }
      return true;
    }
    

    八、类初始化

          查看link_class_impl方法的调用链可知类初始化的入口是InstanceKlass::initialize_impl方法,如下图:

    该方法的关键代码如下:

    void InstanceKlass::initialize_impl(instanceKlassHandle this_oop, TRAPS) {
      //完成此类的链接,如果已链接则会立即返回
      this_oop->link_class(CHECK);
    
      DTRACE_CLASSINIT_PROBE(required, InstanceKlass::cast(this_oop()), -1);
    
      bool wait = false;
    
      // Step 1
      {
        //获取对象锁
        oop init_lock = this_oop->init_lock();
        ObjectLocker ol(init_lock, THREAD, init_lock != NULL);
    
        Thread *self = THREAD; // it's passed the current thread
    
        // Step 2
        //如果正在初始化则等待初始化完成
        while(this_oop->is_being_initialized() && !this_oop->is_reentrant_initialization(self)) {
            wait = true;
          ol.waitUninterruptibly(CHECK);
        }
    
        //等待超时返回
        if (this_oop->is_being_initialized() && this_oop->is_reentrant_initialization(self)) {
          DTRACE_CLASSINIT_PROBE_WAIT(recursive, InstanceKlass::cast(this_oop()), -1,wait);
          return;
        }
    
        //初始化完成返回
        if (this_oop->is_initialized()) {
          DTRACE_CLASSINIT_PROBE_WAIT(concurrent, InstanceKlass::cast(this_oop()), -1,wait);
          return;
        }
    
        //状态异常,抛出异常
        if (this_oop->is_in_error_state()) {
          DTRACE_CLASSINIT_PROBE_WAIT(erroneous, InstanceKlass::cast(this_oop()), -1,wait);
          ResourceMark rm(THREAD);
          const char* desc = "Could not initialize class ";
          const char* className = this_oop->external_name();
          size_t msglen = strlen(desc) + strlen(className) + 1;
          char* message = NEW_RESOURCE_ARRAY(char, msglen);
          if (NULL == message) {
            // Out of memory: can't create detailed error message
            THROW_MSG(vmSymbols::java_lang_NoClassDefFoundError(), className);
          } else {
            jio_snprintf(message, msglen, "%s%s", desc, className);
            THROW_MSG(vmSymbols::java_lang_NoClassDefFoundError(), message);
          }
        }
    
        // 设置状态,初始化进行中
        this_oop->set_init_state(being_initialized);
        this_oop->set_init_thread(self);
      }
    
      // Step 7
      //如果不是一个接口而是一个类则需要初始化它的父类
      if (!this_oop->is_interface()) {
         //获取父类
        Klass* super_klass = this_oop->super();
        //初始化父类
        if (super_klass != NULL && super_klass->should_be_initialized()) {
          super_klass->initialize(THREAD);
        }
        //实现的接口存在默认方法则初始化接口
        if (!HAS_PENDING_EXCEPTION && this_oop->has_default_methods()) {
          this_oop->initialize_super_interfaces(this_oop, THREAD);
        }
    
        //初始化异常,抛出异常
        if (HAS_PENDING_EXCEPTION) {
          Handle e(THREAD, PENDING_EXCEPTION);
          CLEAR_PENDING_EXCEPTION;
          {
            EXCEPTION_MARK;
            this_oop->set_initialization_state_and_notify(initialization_error, THREAD);
            CLEAR_PENDING_EXCEPTION;
          }
          DTRACE_CLASSINIT_PROBE_WAIT(super__failed, InstanceKlass::cast(this_oop()), -1,wait);
          THROW_OOP(e());
        }
      }
    
      // Step 8
      {
        assert(THREAD->is_Java_thread(), "non-JavaThread in initialize_impl");
        JavaThread* jt = (JavaThread*)THREAD;
        DTRACE_CLASSINIT_PROBE_WAIT(clinit, InstanceKlass::cast(this_oop()), -1,wait);
        //执行静态方法
        this_oop->call_class_initializer(THREAD);
      }
    
      // Step 9
      if (!HAS_PENDING_EXCEPTION) {
        //设置状态初始化完成
        this_oop->set_initialization_state_and_notify(fully_initialized, CHECK);
        { ResourceMark rm(THREAD);
          debug_only(this_oop->vtable()->verify(tty, true);)
        }
      }
      else {
        //初始化失败,抛出异常
        Handle e(THREAD, PENDING_EXCEPTION);
        CLEAR_PENDING_EXCEPTION;
        JvmtiExport::clear_detected_exception((JavaThread*)THREAD);
        {
          EXCEPTION_MARK;
          this_oop->set_initialization_state_and_notify(initialization_error, THREAD);
          CLEAR_PENDING_EXCEPTION;   // ignore any exception thrown, class initialization error is thrown below
          JvmtiExport::clear_detected_exception((JavaThread*)THREAD);
        }
        DTRACE_CLASSINIT_PROBE_WAIT(error, InstanceKlass::cast(this_oop()), -1,wait);
        if (e->is_a(SystemDictionary::Error_klass())) {
          THROW_OOP(e());
        } else {
          JavaCallArguments args(e);
          THROW_ARG(vmSymbols::java_lang_ExceptionInInitializerError(),
                    vmSymbols::throwable_void_signature(),
                    &args);
        }
      }
      DTRACE_CLASSINIT_PROBE_WAIT(end, InstanceKlass::cast(this_oop()), -1,wait);
    }

     

    展开全文
  • 《深入学习c++string》2.1版

    热门讨论 2009-12-07 22:28:10
    附录2: MSSTL中basic_string的部分源码解读 24 2.1 string的allocator 24 2.1.1 Allocate和Deallocate 24 2.1.2 allocator的泛型实现 24 2.1.3 string与char_traits 24 2.1.4 以char和wchar_t特化char_traits 24 ...
  • String str2 = new StringBuilder("计算机").append("技术").toString(); System.out.println(str2 == str2.intern()); String s2 = new StringBuilder("计算机技术").toString(); System.out.println(s
  • C++ - unordered_map 源码解析

    千次阅读 2017-03-05 17:29:04
      主要尝试回答下面几个问题: 一般情况下,使用 hash 结构,需要有桶的概念,那么 unordered_map 是如何自动管理桶的,这个问题其实再细分的话是这样的: ...对于 string,unordered_map 的默认哈希
  • 标准库类型之string类 常见面试题:实现一个 String
  • c++ string 和 stl算法

    千次阅读 2008-12-25 20:33:00
    toupper,tolower地球人都知道 C++string 没有 toupper ,好在这不是个大问题,因为我们有 STL 算法:string s("heLLo");transform(s.begin(), s.end(), s.begin(), ::toupper);cout transform(s.begin(), s.end...
  • String接口和属性String类是final不可继承的,用来存放字符数组的value[]也是final修饰的,这意味着String类是不可变的,一旦new出来就不能发生改变。因此对一个String类的对象进行任何修改(增加、删除、替换)之后...
  • smartdict:MIPT, C++-源码

    2021-07-14 00:01:50
    文本解析器(句子和单词的选择) class TParser void SplitText(const string & text, vector &句子) const; void SplitSentence (const string & sentence, vector & words) const; ** 2.0 版 ** 制作预过滤器...
  • C++构造和解析Json

    万次阅读 2017-09-06 16:17:53
    概述 JSON是一种轻量级的数据交互格式,易于人阅读和编写,同时也...json解析选用jsoncpp作为第三方库,jsoncpp使用广泛,c++开发首选。 jsoncpp目前已经托管到了github上,地址:https://github.com/open-source...
  • JSON(JavaScript Object Notation)跟xml一样也是一种数据交换格式,了解json请参考其官网http://json.org,本文不再对json做介绍,将重点介绍c++的json解析库的使用方法。 一. 使用jsoncpp解析jsonJsoncpp是个跨...
  • 一个小的 C++ 参数解析器库 如何构建 构建静态库 make -f Makefile.static 构建共享库 make -f Makefile.shared 例子 class PortNumberValidator : public Validator { public: bool validate ( const vector...
  • c++ unordered_map 源码解析

    千次阅读 2016-08-01 23:32:48
    一般情况下,使用 hash 结构,需要有桶的概念,那么 unordered_map 是如何自动管理桶的,这个问题其实再细分的话是这样的: ...代码位于 /usr/include/c++/4.1.2/tr1/,编译器版本比较老,在这个目录下,有这些文件
  • XML 解析c++源码(源文件)

    千次阅读 2010-01-26 14:39:00
    XMLTool.cpp 配置文件解析动态库资源文件 * 主要输出函数接口分别实现对配置文件的读取和写入,针对结果集的复制清空,和两个字符串处理函数 * 读取配置文件接口 :int ReadXMLFile(XML_Node_Vector *...
  • XML 解析c++源码(头文件)

    千次阅读 2010-01-26 14:37:00
    XMLTool.h 配置文件解析动态库头文件 * 主要输出函数接口分别实现对配置文件的读取和写入,针对结果集的复制清空,和两个字符串处理函数 * 读取配置文件接口 :int ReadXMLFile(XML_Node_Vector *XmlNodeVector...
  • String源代码解析

    千次阅读 2018-07-14 00:41:32
    2.string源码解析 3jdk8相对于jdk7的不同 4.补充 二.String中的享元模式 享元模式(Flyweight)可以粗略的理解为缓存(cache),是设计中的一种优化策列。 1.常量与常量池 在这里需要引入常量池这个简单的概念...
  • C++实现Json解析详细教程

    万次阅读 2017-02-23 11:12:38
    在GitHub官网上下载源码https://github.com/open-source-parsers/jsoncpp/ 用cmake-gui生成.sln解决方案,cmake-gui使用教程请参考我以前的博客http://blog.csdn.net/qq_35488967/article/details/56480053 将json-...
  • C++之RapidJSON解析json数据

    千次阅读 2019-05-26 10:20:15
    RapidJSON 是一个 C++ 的 JSON 解析器及生成器。 JSON文本: //document.json { "name": "xiaoming", "gender": "boy", "hobby": [ "足球", "篮球", "电影" ], "score": { "数学": 91.5,...
  • C++的Json解析库:jsoncpp和boost

    万次阅读 热门讨论 2011-12-29 23:57:44
    JSON(JavaScript Object Notation)跟xml一样也是一种数据交换格式,了解json请...json官网上列出了各种语言对应的json解析库,作者仅介绍自己使用过的两种C++的json解析库:jsoncpp(v0.5.0)和Boost(v1.34.0)。  一. 使
  • Android Parcelable序列化源码解析

    千次阅读 2021-04-13 21:56:23
    文章目录序列化数据分析源码相关类整理序列化源码解析Java层Parcel对象的创建过程Native层Parcel对象的创建过程Parcel写入对象解析类型标识符以及类名称的写入变量值的写入native Parcel#writeString16函数解析...
  • jrtplib example1源码解析

    千次阅读 2014-06-01 21:33:56
    源码中的example1.cpp源码解析 /*  Here's a small IPv4 example: it asks for a portbase and a destination and   starts sending packets to that destination.  这个IPv4版的例子:它要求portbase...
  • WCDB源码解析

    千次阅读 2018-03-27 22:13:25
    源文链接:http://xiangwangfeng.com/2018/01/08/WCDB-源码解析起因最近开了个新项目,项目的主程童鞋引入了 WCDB 代替原先自制的 KeyValueStore 和 FMDB。问为何,答曰:好用,线程安全又高效。又问具体实现...
  • 本序列文章的目的是总结一下这段时间所学...runtld.cpp源码解析 3 tld.cpp源码解析 4 LKTracker(重点) 5 FerNNClassifier.cpp源码解析(重点) 6 tld_utils.cpp源码解析  runtld.cpp这个是main函数所在的文件

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 35,107
精华内容 14,042
关键字:

c++string源码解析

c++ 订阅