【发布时间】:2022-01-14 05:31:22
【问题描述】:
Windows、visual studio2017、C++、gRPC-1.40.0
代码以hello world为基础,增加如下流程。
- 输入“x”,应用程序将终止,Protocol Buffers 将被释放。
- 输入“x”以外的内容,创建一个通道并向服务器发出请求。
根据输入“x”以外的次数,应用程序结束时检测到的未释放内存量会增加。
-客户-
#include <iostream>
#include <Windows.h>
struct cleanup_t
{
~cleanup_t() { if (IsDebuggerPresent()) _CrtDumpMemoryLeaks(); }
} cleanup;
#include <memory>
#include <string>
#include <grpcpp/grpcpp.h>
#include <grpc/support/log.h>
#include "../common/helloworld.grpc.pb.h"
using grpc::Channel;
using grpc::ClientAsyncResponseReader;
using grpc::ClientContext;
using grpc::CompletionQueue;
using grpc::Status;
using helloworld::HelloRequest;
using helloworld::HelloReply;
using helloworld::Greeter;
class GreeterClient{
public:
explicit GreeterClient(std::shared_ptr<Channel> channel)
: stub_(Greeter::NewStub(channel)) {}
std::string SayHello(const std::string& user){
HelloRequest request;
request.set_name(user);
HelloReply reply;
ClientContext context;
Status status = stub_->SayHello(&context, request, &reply);
if (status.ok()) {
return reply.message();
}
else {
std::cout << status.error_code() << ": " << status.error_message() << std::endl;
return "RPC failed";
}
}
private:
std::unique_ptr<Greeter::Stub> stub_;
};
int main(int argc, char** argv){
std::cout << "****************" << std::endl;
std::cout << "Start Client" << std::endl;
std::cout << "Finish:x" << std::endl;
std::cout << "****************\n" << std::endl;
bool exit = false;
while ( !exit ){
char buffer[128];
printf(">");
fgets(buffer, 128, stdin);
if (strcmp(buffer, "x\n") == 0) {
exit = true;
}
else{
GreeterClient greeter(grpc::CreateChannel("localhost:50051", grpc::InsecureChannelCredentials()));
std::string reply = greeter.SayHello("World"); // The actual RPC call!
std::cout << "Greeter received: " << reply << std::endl;
}
}
google::protobuf::ShutdownProtobufLibrary();
return 0;
}
-服务器-
#include <iostream>
#include <memory>
#include <string>
#include <grpcpp/ext/proto_server_reflection_plugin.h>
#include <grpcpp/grpcpp.h>
#include <grpcpp/health_check_service_interface.h>
#ifdef BAZEL_BUILD
#include "examples/protos/helloworld.grpc.pb.h"
#else
#include "../common/helloworld.grpc.pb.h"
#endif
using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::Status;
using helloworld::Greeter;
using helloworld::HelloReply;
using helloworld::HelloRequest;
class GreeterServiceImpl final : public Greeter::Service {
Status SayHello(ServerContext* context, const HelloRequest* request,
HelloReply* reply) override {
std::string prefix("Hello ");
reply->set_message(prefix + request->name());
return Status::OK;
}
};
void RunServer() {
std::string server_address("0.0.0.0:50051");
GreeterServiceImpl service;
grpc::EnableDefaultHealthCheckService(true);
grpc::reflection::InitProtoReflectionServerBuilderPlugin();
ServerBuilder builder;
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
builder.RegisterService(&service);
std::unique_ptr<Server> server(builder.BuildAndStart());
std::cout << "Server listening on " << server_address << std::endl;
server->Wait();
}
int main(int argc, char** argv) {
RunServer();
return 0;
}
还在服务器端增加了一个可以检测内存泄漏的进程,经过测试,确认每次连接时服务器的内存也增加了。
顺便说一句,你可能认为不应该每次都创建GreeterClient 类并抛出请求,但是下面的代码也会发生内存泄漏。
-产生内存泄漏的过程-
- 在客户端运行的情况下重新启动服务器。
- 输入“x”以外的内容。
根据执行上述步骤的次数,在应用程序结束时检测到的未释放内存量会增加。
-客户-
#include <iostream>
#include <Windows.h>
struct cleanup_t
{
~cleanup_t() { if (IsDebuggerPresent()) _CrtDumpMemoryLeaks(); }
} cleanup;
#include <memory>
#include <string>
#include <grpcpp/grpcpp.h>
#include <grpc/support/log.h>
#include "../common/helloworld.grpc.pb.h"
using grpc::Channel;
using grpc::ClientAsyncResponseReader;
using grpc::ClientContext;
using grpc::CompletionQueue;
using grpc::Status;
using helloworld::HelloRequest;
using helloworld::HelloReply;
using helloworld::Greeter;
class GreeterClient{
public:
explicit GreeterClient(std::shared_ptr<Channel> channel)
: stub_(Greeter::NewStub(channel)) {}
std::string SayHello(const std::string& user){
HelloRequest request;
request.set_name(user);
HelloReply reply;
ClientContext context;
Status status = stub_->SayHello(&context, request, &reply);
if (status.ok()) {
return reply.message();
}
else {
std::cout << status.error_code() << ": " << status.error_message() << std::endl;
return "RPC failed";
}
}
private:
std::unique_ptr<Greeter::Stub> stub_;
};
int main(int argc, char** argv){
std::cout << "****************" << std::endl;
std::cout << "Start Client" << std::endl;
std::cout << "Finish:x" << std::endl;
std::cout << "****************\n" << std::endl;
GreeterClient greeter(grpc::CreateChannel("localhost:50051", grpc::InsecureChannelCredentials()));
bool exit = false;
while ( !exit ){
char buffer[128];
printf(">");
fgets(buffer, 128, stdin);
if (strcmp(buffer, "x\n") == 0) {
exit = true;
}
else{
std::string reply = greeter.SayHello("World"); // The actual RPC call!
std::cout << "Greeter received: " << reply << std::endl;
}
}
google::protobuf::ShutdownProtobufLibrary();
return 0;
}
我认为每次重新连接并抛出请求时都会发生内存泄漏。
正在发生内存泄漏。 附图显示了使用性能监视器在以下代码中监视客户端内存的结果。 内存监控在启动后一段时间开始。
int main(int argc, char** argv){
std::cout << "****************" << std::endl;
std::cout << "Start Client" << std::endl;
std::cout << "Finish:x" << std::endl;
std::cout << "****************\n" << std::endl;
bool exit = false;
while ( !exit ){
char buffer[128];
printf(">");
fgets(buffer, 128, stdin);
if (strcmp(buffer, "x\n") == 0) {
exit = true;
}
else{
while (true)
{
GreeterClient greeter(grpc::CreateChannel("localhost:50051", grpc::InsecureChannelCredentials()));
std::string reply = greeter.SayHello("World"); // The actual RPC call!
Sleep(1);
}
}
}
google::protobuf::ShutdownProtobufLibrary();
return 0;
}
【问题讨论】:
标签: c++ visual-c++ memory-leaks grpc grpc-c++